home *** CD-ROM | disk | FTP | other *** search
/ Hottest 6 / Hottest 6 (1996)(PDSoft)[!].iso / software / programming / c / shadow / docs / introduction.doc < prev    next >
Text File  |  1978-11-24  |  204KB  |  4,741 lines

  1.                        Shadow Development Documentation
  2.                             Library Version 5.0
  3.  
  4.                               By David Navas
  5.                          Updated:  13 Nov 1992
  6.  
  7.                       Copyright © 1992 by David Navas
  8.                          with help from Karen Lien
  9.                             All Rights Reserved
  10.  
  11.  
  12.                                 THE PLAN
  13.      Basic Concepts
  14.      History
  15.      Overview
  16.      Resource Tracking
  17.         Dual-pass resource tracking
  18.      System String Constants
  19.      Semaphores
  20.         Intra-Process Locking Integrity
  21.         Counting Semaphores
  22.         Conditional Variables
  23.         Multi-Process Signalling
  24.         Performance Issues
  25.      AvlTrees
  26.         Performance Issues
  27.         Summary
  28.      SLists
  29.      Memory
  30.      OOPL
  31.      Object Oriented
  32.      Object-Orientedness under SHADOW
  33.         Creating Classes
  34.         AttributeTags
  35.         MethodTags
  36.            mtag_defnObject
  37.            mtag_procObject
  38.            mtag_flags
  39.            mtag_threadstat
  40.            mtag_priority
  41.            mtag_method
  42.            mtag_arguments
  43.         ArgumentTags
  44.         CreateInstance
  45.         Wrap-up of Methods
  46.      Patcher Class
  47.      Director Class
  48.      Process Class
  49.      Process and Program Shutdown
  50.      Conclusion
  51.  
  52.  
  53.                               BASIC CONCEPTS
  54.  
  55.           SHADOW is a concurrent-object-oriented addition to AmigaDOS.
  56.      Its principle design goal is to help standardize an extensible
  57.      environment paradigm.  It takes advantage of some of the better
  58.      AmigaDOS facilities (shared memory system, IPC ports, and fast
  59.      context switching) by internally managing much of the inter-task
  60.      communications, resource tracking, and resource allocation.
  61.  
  62.           Traditional object-oriented systems separate function
  63.      interfaces from internal data structures and manage the allocation
  64.      and access of these structures within objects.  SHADOW takes this
  65.      interface separation one step further by partially uncoupling the
  66.      parameter specification at method invocation from the parameter
  67.      specification of the method implementation.
  68.  
  69.           Because of this relatively high separation, argument type-
  70.      checking is not implemented under SHADOW.  To implement
  71.      type-checking in a run-time bound environment almost requires
  72.      run-time type-checking.  It was decided that this was too
  73.      expensive a use of computing resources.  Under AmigaOS, there is
  74.      some precedence for this;  for example, it is difficult to type-check
  75.      TagItem strutures.
  76.  
  77.           While SHADOW's main goal is to provide a unified interface
  78.      for distributed, extensible, multi-threaded computing, there are
  79.      many provisions for speed, this being just one of them.  Other
  80.      examples include callback hooks for method dispatching and attribute
  81.      lookups; if you feel SHADOW's versions are not quite what you want
  82.      in either speed or features or both, you can set these hooks on a
  83.      class-by-class basis.  Additionally, attributes are created in such a
  84.      manner as to provide multiple ways of getting at object instance
  85.      variables -- including (but not limited to) the use of more "normal"
  86.      C-structure dereferencing.  While this type of dereferencing isn't
  87.      guaranteed if you're using someone else's class (or, even, if you
  88.      are subclassed off of someone else's class), it does provide a
  89.      very fast way of getting at attributes, and SHADOW uses it in
  90.      several places within its internal code for the sake of efficiency.
  91.  
  92.  
  93.                                    HISTORY
  94.  
  95.           SHADOW was created to solve the problems which I ran into with
  96.      my first programming project -- JazzBench.  That experience taught
  97.      me that the most important thing in a co-operative multi-program
  98.      environment is flexibility.  You need to be able to change the
  99.      behaviour of EVERYTHING -as- -it- -runs-.  This lesson was the
  100.      principle reason behind the initial design of SHADOW, and the result
  101.      of that principle was the entire WatchedVariable construct. To a
  102.      lesser extent, it was also responsible for Patches.
  103.  
  104.           However, that was not the only lesson that was learned.  Trying
  105.      to locate governing control within a single server not only created
  106.      bottleneck problems, but also, in the end, either limited or
  107.      complicated the design process for my programs.  What I needed was a
  108.      subsystem which effectively dealt with the concurrency and shared
  109.      resource management problems across a distributed environment.
  110.  
  111.           The other major subsystems of SHADOW support the increased use
  112.      of more complicated data structures.  Exec lists are very fast at
  113.      some things, but searching and sorting are definitely not their
  114.      strong points.
  115.  
  116.  
  117.  
  118.  
  119.                                  OVERVIEW
  120.  
  121.      This introduction is an attempt to acquaint the reader with SHADOW's
  122.      capabilities and limitations, and to introduce the programming
  123.      paradigm it offers to Amiga programmers.
  124.  
  125.      The number of functions in SHADOW is truly daunting.  However, only a
  126.      very few need to be well-understood in order to program SHADOW
  127.      effectively.  These functions fall into about seven categories, which
  128.      we will take up in turn.  The following is an informal introduction
  129.      to familiarize you with the terminology and the pertinent function
  130.      calls in SHADOW.  Confusing terms can (hopefully) be found in
  131.      Glossary.doc.  If they can't be, please let me know what they are, and
  132.      they will appear in future versions of that documentation file.
  133.  
  134.  
  135.  
  136.  
  137.  
  138.                          RESOURCE TRACKING CALLS
  139.  
  140.           SHADOW gives programmers the ability to track resources, and
  141.      in fact forces them to do so.  Objects are NEVER freed until the
  142.      useCount drops to zero.  Objects are often returned from functions
  143.      as Use()'d, meaning that the programmer needs to specifically tell
  144.      the computer when the resource is no longer needed.
  145.  
  146.      Consider the following example:
  147.  
  148.           /*
  149.            * Find an object inside of an avltree.
  150.            */
  151.           myobject = FindTreeStringNode(avltree, MY_OBJECT_NAME);
  152.           .
  153.           .
  154.           .
  155.  
  156.           In this example, a resource that was stored under the
  157.      "MY_OBJECT_NAME" is looked up in a tree and returned to a program.
  158.      Remember that SHADOW is primarily a tool for inter-process sharing
  159.      of objects.  At any time, some other program might come along,
  160.      remove your object from your avltree and try and destroy (free)
  161.      that object.  To prevent this from happening, the FindTreeNode()
  162.      code (on top of which the FindTreeStringNode() code is written)
  163.      returns the object it finds "UseObject()'d", so that some other
  164.      program cannot pull the object out from under you.  It falls to you,
  165.      the programmer, to tell the system when you are done using the
  166.      returned object.
  167.  
  168.           /*
  169.            * Find an object inside of an avltree.
  170.            */
  171.           myobject = FindTreeStringNode(avltree, MY_OBJECT_NAME);
  172.           .
  173.           . <<Read/modify the object>>
  174.           .
  175.           DropObject(myobject); /* No longer interested in myobject */
  176.  
  177.      After the DropObject() call, the myobject pointer should be
  178.      considered invalid, and never used thereafter.
  179.  
  180.           There are several variations on this theme.  The examples we
  181.      will cover are as follows:
  182.           1) Returning objects implies that you must have a a valid
  183.               use()d pointer already, and that the function you return
  184.               to handles the resouce-tracking DropObject() call properly.
  185.           2) Functions declared as returning void * may, in fact, return
  186.               objects that require them to be resouce tracked.
  187.           3) Some functions "swallow" resources.  DropObject() is one
  188.               example, but there are others.
  189.  
  190.  
  191.           Consider the example code above.  Imagine if, instead of
  192.      performing all reads and modifications on the objects between
  193.      the FindTreeStringNode() code and the DropObject() code, we wished
  194.      to return the object to a calling function for further processing:
  195.  
  196.           DoSomething()
  197.           {
  198.              OBJECT myobject;
  199.  
  200.              /*
  201.               * First retrieve and preprocess the object in some
  202.               * manner, then return to me for further processing.
  203.               */
  204.              myobject = getMyLocalObject();
  205.              .
  206.              . <<Read/modify the object a bit here>>
  207.              .
  208.              DropObject(myobject); /* No longer interested */
  209.           }
  210.  
  211.           .
  212.           .
  213.           .
  214.  
  215.           OBJECT getMyLocalObject()
  216.           {
  217.              OBJECT myobj;
  218.  
  219.              /*
  220.               * Find an object inside of an avltree.
  221.               */
  222.              myobj = FindTreeStringNode(avltree, MY_OBJECT_NAME);
  223.              .
  224.              . <<Read/modify the object>>
  225.              .
  226.              return myobj;
  227.           }
  228.  
  229.  
  230.      Notice that getMyLocalObject() does NOT call DropObject() on myobj.
  231.      This is because the resource is still needed when it is returned
  232.      from getMyLocalObject().  The object is truly no longer needed
  233.      only when it is no longer referred to within a sequence of execution.
  234.      Thus, it is DoSomething() that actually calls DropObject(), and not
  235.      getMyLocalObject().
  236.  
  237.           Now consider the DoShadow() call.  It is prototyped as returning
  238.      a "void *".  Surely many method calls will return other types of data!
  239.      Methods may returns strings, numbers, pointers to structures, objects,
  240.      etc.  It is this last case that we are interested in.
  241.  
  242.           DoSomething()
  243.           {
  244.              OBJECT myobject;
  245.  
  246.              /*
  247.               * First retrieve and preprocess the object in some
  248.               * manner, then return to me for further processing.
  249.               */
  250.              myobject = DoShadow(controlObject,
  251.                                  NULL,
  252.                                  METH_GET_LOCAL_OBJECT,
  253.                                  METHOD_END);
  254.              .
  255.              . <<Read/modify the object a bit here>>
  256.              .
  257.              DropObject(myobject); /* No longer interested */
  258.           }
  259.  
  260.           .
  261.           .
  262.           .
  263.  
  264.           /*
  265.            * The method declaration (in part).
  266.            */
  267.           ARG_TAG   REF_getMyLocalObject[] = {RET_OBJ};
  268.  
  269.           OBJECT getMyLocalObject(METHOD_ARGS)
  270.           {
  271.              OBJECT myobj;
  272.  
  273.              /*
  274.               * Find an object inside of an avltree.
  275.               */
  276.              myobj = FindTreeStringNode(avltree, MY_OBJECT_NAME);
  277.              .
  278.              . <<Read/modify the object>>
  279.              .
  280.              return myobj;
  281.           }
  282.  
  283.      The method declaration almost exactly parallels the function
  284.      declaration, except that there is an extra ARG_TAG which
  285.      describes what the method expects as arguments and what the method
  286.      returns.
  287.  
  288.           This method declaration will work for any kind of method call.
  289.      Consider a method invoked synchronously across processes.  The
  290.      method does not drop one from the usecount of the object and instead
  291.      passes the object pointer back to the calling process.  The calling
  292.      process then calls DropObject().  You can see that the resource is
  293.      effectively transferred from one process to another (although that
  294.      procedure is not currently recorded anywhere within these processes).
  295.  
  296.           Consider the asynchronous case.  While invoking this method
  297.      asynchronously is of little to no value, it can happen under
  298.      certain error conditions (for instance, if you are invoking a
  299.      synchronous inter-process method from a process without an associated
  300.      SHADOW process object).  In this case, the ARG_TAG informs the
  301.      method parsing routines (ParseShadowMessage()) to properly discard
  302.      the returned object.  The DoShadow() method invocation returns
  303.      NULL (as with all asynchronous method invocations).  DropObject()
  304.      correctly handles the NULL input, however your Read/modify code needs
  305.      to handle this case as well!
  306.  
  307.           One more example of the method invocation is included for
  308.      completeness.  Let's say you had a window class that you were
  309.      creating on top of the included ROOT_CLASS.  The METH_INIT
  310.      method returns a window object which is not only "Use()d" in the
  311.      local sequence of execution sense, but is also placed on various
  312.      system lists.
  313.  
  314.           OBJECT WinInitMethod(METHOD_ARGS, <window-args>);
  315.           {
  316.              /*
  317.               * Some window initialization.
  318.               */
  319.  
  320.              SetMethodEnd(...);    /* Set the METHOD_END in the
  321.                                       function's argument stack
  322.                                       correctly for the superclass'
  323.                                       method. */
  324.  
  325.              return CallSuper();
  326.           }
  327.  
  328.      Thus when you call CreateInstance()...:
  329.  
  330.           windowObj = CreateInstance(NULL, WINDOW_CLASS,
  331.                                            META_CLASS,
  332.                                            <window-args>
  333.                                            METHOD_END);
  334.  
  335.           .
  336.           .
  337.           .
  338.  
  339.      ...you will eventually need to both tell the system not to "use"
  340.      windowObj locally, AND not to use the windowObj anywhere in the
  341.      system lists.
  342.  
  343.           .
  344.           .
  345.           .
  346.           /*
  347.            * No longer need windowObj locally.
  348.            */
  349.           DropObject(windowObj);
  350.  
  351.           .
  352.           .
  353.           .
  354.  
  355.           /*
  356.            * Cleanup code!
  357.            */
  358.           windowObj = getMyLocalWindow();
  359.           RemoveObject(windowObj);
  360.  
  361.  
  362.           RemoveObject() is a linked library call which serves as a
  363.      convenient combination of two function calls.  First, a
  364.      METH_REMOVE is sent to the passed object.  This informs the
  365.      system to Remove the object from all system lists (for more
  366.      details on METH_REMOVE, see below).  The windowObj is then
  367.      passed to the DropObject() function, which removes the
  368.      object from the local sequence of execution.  Thus, when
  369.      RemoveObject() returns, the windowObj is no longer a valid
  370.      window pointer, in exactly the same way that it wouldn't be
  371.      valid if passed to DropObject().
  372.  
  373.           RemoveObject() is an example of a function which
  374.      "swallows" an object pointer from the local sequence of execution.
  375.      DropObject() is an example as well.  Another example is the
  376.      convenience function SetObject() which transfers ownership of the
  377.      passed object from the local sequence of execution to a shared
  378.      section of memory and returns the object that used to be at that
  379.      memory location back to the program in an atomic fashion.
  380.  
  381.      If, at anytime, you want to retain a local pointer to the object
  382.      that you pass into one of these functions, you may use the code as in
  383.      the following examples:
  384.  
  385.           RemoveObject(UseObject(windowObj);
  386.  
  387.                -or-
  388.  
  389.           oldObject = SetObject(&memoryLocation, UseObject(newObject));
  390.  
  391.      UseObject() increases the usecount of the pointer (and also returns
  392.      the pointer to the object, which allows for the easy, if slightly
  393.      confusing, nesting).  At some later point you will have to
  394.      remember to DropObject() the objects, of course!
  395.  
  396.      The following, slightly less confusing, examples do the same thing:
  397.  
  398.           UseObject(windowObj);
  399.           RemoveObject(windowObj);
  400.  
  401.                -or-
  402.  
  403.           windowObj = UseObject(windowObj);
  404.           RemoveObject(windowObj);
  405.  
  406.                -or-
  407.  
  408.           newObject = UseObject(newObject);
  409.           oldObject = SetObject(&memoryLocation, newObject);
  410.  
  411.           etc.
  412.  
  413.  
  414.           In addition to the UseObject() and DropObject() calls for
  415.      SHADOW's objects, there are analogous functions for string
  416.      constants.  These string constants, herein referred to as either
  417.      system strings or unique strings, will now be discussed.  The
  418.      pertinent calls to resource tracking are UseString() and DropString(),
  419.      which perform reasonably analogously to UseObject() and DropObject().
  420.  
  421.  
  422.      DUAL-PASS RESOURCE TRACKING
  423.  
  424.           The distinction between DropObject()ing a resource and
  425.      RemoveObject()ing is more significant than you might suspect at
  426.      first glance.  The main problem with using useCounts in any
  427.      system is the possibility of a self-referencing object, or
  428.      a self-referencing chain of objects.  That is, object1
  429.      contains a field which points to object1 -- in which case
  430.      the useCount will never drop to zero; or object1 contains a
  431.      pointer to object2 that contains a pointer to object3 that contains
  432.      a pointer back to object1.  All three objects' useCounts, in this
  433.      case, will never fall to zero.
  434.  
  435.           To avoid this problem, SHADOW objects should use the following
  436.      dual-pass algorithm:
  437.           1) In the METH_REMOVE method for each object, all references to
  438.              other objects should either disappear, or be guaranteed to
  439.              never participate in a self-referencing "ring".
  440.           2) After a METH_REMOVE method is received by an object, it
  441.              must never CREATE or allow the creation of, a
  442.              self-referencing "ring" in which it is a participant.
  443.           3) When a METH_DESTROY is sent, the object should be destroyed.
  444.  
  445.           Self-referencing may occur, therefore, only between the
  446.           METH_INIT call to an object, and the METH_REMOVE call.
  447.  
  448.           This allows self-referencing to exist within your programs, but
  449.      defines a procedure for eliminating all of these self-references
  450.      without resorting to something time-consuming like garbage
  451.      collection.  The METH_REMOVE pass is often referred to as the
  452.      first pass in the dual-pass resource tracking algorithm, and the
  453.      METH_DESTROY is often referred to as the second pass in the dual-
  454.      pass resource tracking algorithm.  It is up to the individual
  455.      programmer to design his or her classes to conform to these
  456.      specifications.
  457.  
  458.           Nearly all of the above refers to the GLOBAL resource tracking
  459.      facilities of SHADOW.  SHADOW also provides a LOCAL resource tracking
  460.      facility, which helps to reduce the clean-up code (at the expense of
  461.      some init code).  When a resource is allocated that you want to be
  462.      freed when the process exits, you can pass a pointer to the object
  463.      (must be a SHADOW object!) to the AddAutoResource().
  464.      AddAutoResource() is another example of a procedure that "swallows"
  465.      a resource.
  466.  
  467.           If that resource is later sent a METH_REMOVE by your program
  468.      before the program actually exits, you should remove the reference
  469.      to it within the program with RemoveAutoResource().  Please see the
  470.      AutoDocs for more details.  The example code may also be helpful,
  471.      although not all of it relies on auto-tracked resources.
  472.  
  473.           When the METH_REMOVE method is sent to your program's process
  474.      object, all local resources that were added (via AddAutoResource())
  475.      are sent a METH_REMOVE, and are then freed from the process' resource
  476.      tree.  The RemoveCurrentProgram() sends a METH_REMOVE to
  477.      your program's process, for example.
  478.  
  479.  
  480.                         SYSTEM STRING CONSTANTS
  481.  
  482.           SHADOW uses the concept of unique strings as borrowed from the
  483.      NeXT platform.  Each string is assigned a unique address by the
  484.      system.  Two or more strings of the same contents have the same
  485.      system address.  This allows the system to find strings faster --
  486.      by comparing the address, rather than the contents.
  487.  
  488.           The implementation is via the UseString/DropString/FindString
  489.      library functions, as well as the QuickUseString and QuickDropString
  490.      functions.  System strings are stored on a 1024 entry hash table
  491.      where collisions are linked by a sorted binary tree.  This table is
  492.      found in ShadowBase->sb_systemStrings.
  493.  
  494.           UseString() will lookup the passed string in the table.  If it
  495.      does not find it, the string is created and stored into the table.
  496.      Otherwise, the object's usecount of the located system string is
  497.      incremented.  The system string's address is then returned.
  498.  
  499.           DropString() will lookup the passed string in the table, and,
  500.      if found, the string's useCount is decremented.  If the usecount has
  501.      dropped to zero, the string object is freed and removed from the
  502.      system string list.
  503.  
  504.           FindString() returns the address of a requested system string,
  505.      if one can be found.  It will not increment the usecount, and
  506.      therefore should only be used as an address, rather than as an
  507.      actual string pointer, as that string may go away unexpectedly
  508.      at any time.
  509.  
  510.           QuickUseString() and QuickDropString() take known system string
  511.      addresses and manually increments or decrements the usecount.  If
  512.      the usecount drops to zero, the normal DropString() is used to
  513.      remove the string from the system.  Be sure that you actually
  514.      have a system string pointer before claling these functions!  These
  515.      functions exist for performance reasons only.  It is preferrable
  516.      to go through the UseString() and DropString() calls instead.
  517.  
  518.           Consider the following:
  519.  
  520.                struct StringData {
  521.                     char *sd_name;
  522.                     ULONG sd_data;
  523.                };
  524.                struct StringData MyArray[] = {UseString(-blah-), 1,
  525.                                               UseString(-blah-), 2,
  526.                                               etc.};
  527.  
  528.  
  529.  
  530.      We can now qsort the array on the sd_name field and lookups can be
  531.      as fast as calling a radix search function!
  532.  
  533.           /*
  534.            * Look up item in qsort()'d MyArray
  535.            *
  536.            * [You can find a decent Radix_Search function in nearly
  537.            *  every computer algorithms textbook.]
  538.            */
  539.           Radix_Search(&MyArray, 0,
  540.                        sizeof(MyArray)/sizeof(struct StringData),
  541.                        UseString(look_for_this_string));
  542.           DropString(look_for_this_string);
  543.  
  544.           .
  545.           .
  546.           .
  547.  
  548.           /*
  549.            * free all the strings.
  550.            * Clean up!
  551.            */
  552.           for(i = 0; i < sizeof(MyArray)/sizeof(struct StringData), i++)
  553.                DropString(MyArray[i].sd_name);
  554.  
  555.  
  556.      Not only does this improve access by making an O(n) process into an
  557.      O(log n) one, but the entire string compare overhead is eliminated
  558.      by calling UseString() once (instead of strcmp() 'n' times)!
  559.  
  560.      System strings are used extensively by the object-oriented subsystem
  561.      for such things as lookups of methods and attributes, and for passing
  562.      strings ('JSTR's) between processes during asynchronous method calls.
  563.      They are also used by the AVL tree subsystem which will be covered
  564.      just as soon as we treat the semaphore subsystem.
  565.  
  566.  
  567.  
  568.                               SEMAPHORES
  569.  
  570.      This discussion of SHADOW's semaphores closely follows the
  571.      RKM:Libraries Third Edition's discussion of Exec's semaphores.  It is
  572.      assumed in this section that you have either read this chapter or
  573.      learned about Exec semaphores from some other source, and that you
  574.      understand the principle concepts behind semaphores.
  575.  
  576.      Understanding semaphores is a prerequisite to understanding a lot of
  577.      the deeper issues within SHADOW.  This is why I treat it very close
  578.      to the beginning of this introduction.  If you do not understand
  579.      semaphores, it is highly recommended that you either buy the
  580.      RKM:Libraries book, or some general purpose Operating Systems
  581.      textbook.
  582.  
  583.      Like Exec's semaphoring system, SHADOW's semaphoring system contains
  584.      the basic Shared and Exclusive access constructs.  Under Exec, the
  585.      following calls provide these access constructs:
  586.           AttemptSemaphore(<semaphore>)
  587.           ObtainSemaphore(<semaphore>)
  588.           ObtainSemaphoreShared(<semaphore>)
  589.           ReleaseSemaphore(<semaphore>)
  590.  
  591.      The analogous calls in SHADOW are as follows:
  592.           PSem(<address>, SSEM_ATTEMPT | SSEM_LOCK);
  593.           PSem(<address>, SSEM_LOCK);
  594.           PSem(<address>, SSEM_READ);
  595.           VSem(<address>);
  596.  
  597.      Using the macros defined in <shadow/semaphore.h> these become:
  598.           PSem(<address>, SSEM_ATTEMPT | SSEM_LOCK);
  599.           RWLock(<address>);
  600.           ReadLock(<address>);
  601.           VSem(<address>);
  602.  
  603.  
  604.           However, unlike Exec's semaphoring system, SHADOW does not have
  605.      calls that are directly analogous to the AddSemaphore(),
  606.      RemSemaphore(), FindSemaphore() and InitSemaphore() library calls of
  607.      Exec.  Instead, SHADOW allows you to semaphore any arbitrary address
  608.      (excepting (void *)-1 which is reserved).  The contents at this
  609.      address do not participate in the semaphoring process.  This implies
  610.      that there are no SHADOW semaphore structures analogous to Exec's
  611.      SignalSemaphores. Instead, all legal addresses act as global
  612.      semaphore points.  SHADOW takes care of creating, initializing, and
  613.      destroying all structures needed to internally manage the semaphoring
  614.      process, and because the Amiga's address space is linear across all
  615.      processes, all semaphores are global by nature.  This eliminates the
  616.      need for the AddSemaphore(), the RemSemaphore(), the FindSemaphore(),
  617.      and the InitSemaphore() calls, thus simplifying the programmer's
  618.      task.
  619.  
  620.      Consider two threads running through the following program, one at
  621.      Reader(), one at Writer().
  622.  
  623.           UBYTE *ASharedString;
  624.  
  625.           Reader()
  626.           {
  627.              while(TRUE)
  628.              {
  629.                 ReadLock(ASharedString);
  630.                 printf("The string %s\n", ASharedString);
  631.                 VSem(ASharedString);
  632.              }
  633.           }
  634.  
  635.           Writer()
  636.           {
  637.              while(TRUE)
  638.              {
  639.                WriteLock(ASharedString);
  640.                gets(ASharedString);
  641.                VSem(ASharedString);
  642.              }
  643.           }
  644.  
  645.      "ASharedString" acts as a shared string between the Reader() and
  646.      Writer() threads.  To coordinate access to the string, the
  647.      address of the string is also used as the semaphoring address
  648.      point.  while this is the most straightforward way of managing the
  649.      semaphore system, there is nothing wrong with creating an arbitrary
  650.      semaphoring address point.  For example, you might arbitrate access
  651.      for the string by semaphoring against the Reader()'s function address!
  652.  
  653.           UBYTE *ASharedString;
  654.  
  655.           Reader()
  656.           {
  657.              while(TRUE)
  658.              {
  659.                 ReadLock((void *)Reader);
  660.                 printf("The string %s\n", ASharedString);
  661.                 VSem((void *)Reader);
  662.              }
  663.           }
  664.  
  665.           Writer()
  666.           {
  667.              while(TRUE)
  668.              {
  669.                WriteLock((void *)Reader);
  670.                gets(ASharedString);
  671.                VSem((void *)Reader);
  672.              }
  673.           }
  674.  
  675.      One warning, however.  You are not allowed to semaphore on an address
  676.      that you do not in some way possess.  This is to guard against the
  677.      possibility that some other process gets started that actually -does-
  678.      use this address.  Because SHADOW's semaphoring points are
  679.      automatically public, when this other process attempts to semaphore
  680.      against its address it will come into conflict with your programs.
  681.      This is potentially deadly.
  682.  
  683.      As a corollary, you must release all semaphores that you obtained
  684.      before your program exits and/or before the address that the
  685.      semaphore is arbitrating is freed back to Exec's memory pools.
  686.      To aid in testing, you are directed to the sb_semTree field
  687.      within ShadowBase which acts as the base of all semaphore allocations
  688.      that SHADOW makes.  This field is zero when there are no outstanding
  689.      semaphores.
  690.  
  691.  
  692.           In addition to not having the complications of semaphore
  693.      structures and the (unnecessary, on the Amiga) distinction between
  694.      global and local semaphores, SHADOW does not offer Exec's prepackaged
  695.      ObtainSemaphoreList() and ReleaseSemaphoreList().  While these calls
  696.      are sometimes important to use to avoid deadlock situations, it
  697.      is entirely possible to build your own SemaphoreList wrappers by
  698.      clever use of the SSEM_ATTEMPT flag.  This is left as the proverbial
  699.      exercise to the reader.
  700.  
  701.           Instead, SHADOW expands on Exec's usage of semaphores to
  702.      include intra-process locking integrity, counting semaphores,
  703.      conditional variables, and multi-process signalling.  We take each
  704.      one in turn, and then address performance issues.
  705.  
  706.  
  707.      Intra-Process Locking Integrity
  708.  
  709.           This may be a confusing amalgamation, but the concept is simple.
  710.      Take an example of a library function IterateList().  The purpose
  711.      of IterateList() is to call a function on each of the members on
  712.      a shared list.  We might implement this as follows:
  713.  
  714.           IterateList(struct MyList *list, void (*func)(struct MyNode *node))
  715.           {
  716.                struct MyNode *node;
  717.  
  718.                PSem(list, SSEM_READ);
  719.                node = list->first;
  720.                while(node)
  721.                {
  722.                     func(node);
  723.                     node = node->next;
  724.                }
  725.                VSem(list);
  726.           }
  727.  
  728.      Consider, however, what might happen if we pass a function which
  729.      removed the node from the list it was in.  Clearly this would have
  730.      very bad consequences for IterateList()!
  731.  
  732.      Therefore, instead of the normal Exclusive/Shared access locks,
  733.      SHADOW provides three types of access locks -- an SSEM_READ,
  734.      an SSEM_WRITE, and an SSEM_LOCK (a read-write lock).  SSEM_READ
  735.      locks are shareable across processes and are nestable within
  736.      processes.  SSEM_WRITE is neither shareable nor nestable.  SSEM_LOCK
  737.      is not shareable, but is nestable -- with the following modifications:
  738.  
  739.         a) Multiple SSEM_LOCK requests always nest.
  740.         b) If you request an SSEM_READ on an SSEM_LOCKed semaphore,
  741.              the semaphore becomes a nestable, non-shareable,
  742.              SSEM_READ semaphore.  The semaphore reverts to its earlier
  743.              Read-Write status upon execution of the matching VSem() call.
  744.         c) If you request an SSEM_WRITE on an SSEM_LOCKed semaphore,
  745.              the semaphore becomes a non-nestable, non-shareable,
  746.              SSEM_WRITE semaphore.  Again, the semaphore reverts
  747.              to its earlier Read-Write status upon the matching
  748.              VSem() call.
  749.  
  750.      It is easier to understand this nesting ability by focusing on the
  751.      Read/Write characteristics of these different locks, rather than
  752.      on their Shared/Nestable characteristics.
  753.  
  754.      Any number of processes can Read.  Only one process may Write, and
  755.      no one may be Reading at the time.  The Read-Write is used for
  756.      mutual exclusion across processes and can become either a Read-only
  757.      or a Write-only semaphore as needed -- although the semaphore will
  758.      always remain non-shareable until the last Read-Write semaphore
  759.      access has been released via VSem().
  760.  
  761.      Consider the following example taken from the RemoveResources()
  762.      function in SHADOW:
  763.  
  764.           .
  765.           .
  766.           .
  767.           PSem(&tree, SSEM_LOCK);
  768.           DoInOrderTree(&tree, RemoveCompInstance, NULL);
  769.           FreeTreeAllNodes(&tree);
  770.           VSem(&tree);
  771.           .
  772.           .
  773.           .
  774.  
  775.      Here we are attempting to eliminate each node from a tree and invoke
  776.      the METH_REMOVE method on each node (object) on the tree.  Because of
  777.      the organization of the AVLTree functions (described below), this is
  778.      a two stage process -- we invoke the METH_REMOVEs on all objects in
  779.      the tree (DoInOrderTree()), and then remove all objects from the
  780.      tree (FreeTreeAllNodes()).
  781.  
  782.      As in the above list example, the DoInOrderTree() library function
  783.      locks the tree with an SSEM_READ semaphore call.  The
  784.      FreeTreeAllNodes(), on the other hand, locks the tree with an
  785.      SSEM_WRITE semaphore.
  786.  
  787.      In order to be truly safe, we need to ensure that no other process
  788.      adds a node between the DoInOrderTree() call and the
  789.      FreeTreeAllNodes() call where the tree would normally be accessible.
  790.      To accomplish this, we get an SSEM_LOCK semaphore to surround the two
  791.      function calls.  The SSEM_LOCK provides the nesting that is needed by
  792.      the two library functions which acquire their own semaphores, while
  793.      maintaining the exclusive access characteristics required by the code.
  794.  
  795.      In addition, because SSEM_READ/SSEM_LOCK nesting is different
  796.      from SSEM_LOCK/SSEM_LOCK nesting, the intra-process locking integrity
  797.      is maintained.  Any attempt to gain write or read-write access to the
  798.      tree from within the DoInOrderTree() call will halt the program, and
  799.      any attempt to lock the tree at all within the FreeTreeAllNodes()
  800.      will halt the program.  This allows the programmer to debug the
  801.      problem when it occurs, instead of trying to deduce what occurred
  802.      from problems which might have normally ensued.
  803.  
  804.      Compare that to the alternative as suggested by V39Beta Exec
  805.      semaphores.  Not only does Exec fail to make a distinction
  806.      between Write and Read-Write semaphores, but when you request
  807.      a shared semaphore on a semaphore that you already have locked
  808.      exclusively, you get another exclusive access granted!  This
  809.      precludes the potential debugging advantages of SHADOW's semaphores.
  810.  
  811.      [Of course, it's even worse when you consider that V37 semaphores
  812.       did something entirely different under this exact same set of
  813.       circumstances anyway....]
  814.  
  815.  
  816.      Counting Semaphores
  817.  
  818.      Counting Semaphores are a very straightforward addition to most
  819.      semaphoring systems.  A typical example is with resource
  820.      allocations.  Consider the problem of an audio.device type interface
  821.      where you have four available channels.  All four channels are
  822.      allocated, and a program is waiting for any two.
  823.  
  824.      Here is where counting semaphores come in.  You create a
  825.      counting semaphore by using the CreateSemaphore() macro, passing
  826.      in the maximum number of available resources.  It returns the
  827.      semaphoring address point that you can use in the rest of
  828.      your calls.  You should later free this count semaphore by
  829.      calling DestroySemaphore(), passing in the same address as
  830.      was returned by CreateSemaphore.  The ObtainNumber() and
  831.      ReleaseNumber() macros can be used on this address between
  832.      the Create() and Destroy() calls to obtain and release a
  833.      certain number of resources.
  834.  
  835.           APTR GlobalResourceSemaphore = CreateSemaphore(4);
  836.  
  837.           .
  838.           .
  839.           .
  840.  
  841.                                    /* Obtain two channels. */
  842.           ObtainCount(GlobalResourceSemaphore, 2);
  843.  
  844.           .
  845.           .
  846.           .
  847.  
  848.                           /* release a channel back to the system */
  849.           ReleaseCount(GlobalResourceSemaphore, 1);
  850.  
  851.           .
  852.           .
  853.           .
  854.  
  855.                                    /* Obtain three channels */
  856.           ObtainCount(GlobalResourceSemaphore, 3);
  857.  
  858.           .
  859.           .
  860.           .
  861.  
  862.                               /* Prepare to destroy the semaphore */
  863.           DestroySemaphore(GlobalResourceSemaphore);
  864.  
  865.           /*
  866.            * All resources have been freed, semaphore is now freed
  867.            * as well.
  868.            */
  869.           ReleaseSemaphore(GlobalResourceSemaphore, 4);
  870.  
  871.  
  872.      Notice carefully that you can ask for any number of resources --
  873.      if you ask for too many, you may hang indefinitely, and as there
  874.      is no priority to your semaphore requests, starvation is definitely
  875.      possible.
  876.      Also note that the semaphore is not actually freed from the system
  877.      until all processes release their locks and no process is
  878.      waiting on the semaphore, irregardless of the call to
  879.      DestroySemaphore().
  880.  
  881.      In addition to these calls, you can call the UpCountSem() and
  882.      DownCountSem() macros which create the semaphore automatically
  883.      from the passed address.  This eliminates the need to call the
  884.      CreateSemaphore() and DestroySemaphore() macros, but it limits you
  885.      to single-step increment/decrement calls.
  886.  
  887.  
  888.           ULONG SemaphorePoint;
  889.  
  890.  
  891.                                    /* Obtain two channels of four. */
  892.           UpCount(&SemaphorePoint, 4);
  893.           UpCount(&SemaphorePoint, 4);
  894.  
  895.           .
  896.           .
  897.           .
  898.  
  899.                           /* release a channel back to the system */
  900.           DownCount(&SemaphorePoint);
  901.  
  902.           .
  903.           .
  904.           .
  905.  
  906.                                    /* Obtain three channels of four*/
  907.           UpCount(&SemaphorePoint, 4);
  908.           UpCount(&SemaphorePoint, 4);
  909.           UpCount(&SemaphorePoint, 4);
  910.  
  911.  
  912.           DownCount(&SemaphorePoint);
  913.           DownCount(&SemaphorePoint);
  914.           DownCount(&SemaphorePoint);
  915.           DownCount(&SemaphorePoint);
  916.           /*
  917.            * All resources have been freed, semaphore is now freed
  918.            * as well (automatically).
  919.            */
  920.  
  921.  
  922.      Conditional Variables
  923.  
  924.      Conditional Variables keep track of the number of "conditions" that
  925.      have occurred.  A task can cause any number of conditions, and can
  926.      wait for any number of conditions.
  927.  
  928.      Consider the Reader() and Writer() problem above.  Consider what is
  929.      really happening.  It is quite possible for a single string to be
  930.      printed out between zero and -thousands- of times.  In reality, what
  931.      we want is to have each string printed out once.  Here, the Reader()
  932.      and Writer() example becomes the more classic Consumer() and
  933.      Producer() problem.  Here is an example implementation using
  934.      conditional variables:
  935.  
  936.           extern void Producer(void);
  937.  
  938.           UBYTE *ASharedString;
  939.  
  940.           void main(void)
  941.           {
  942.                SetCondition(((char *)Producer) + 1, 1)
  943.                     /*
  944.                      * Default to produce the first string.
  945.                      * We can obviously default to consuming the
  946.                      * first string as well....
  947.                      */
  948.  
  949.                startpThreads();
  950.           }
  951.  
  952.  
  953.           void Consumer(void)
  954.           {
  955.              while(TRUE)
  956.              {
  957.                 WaitCondition(((char *)Consumer) + 1, 1);
  958.                     /*
  959.                      * Wait for a new string.
  960.                      */
  961.                 printf("The string %s\n", ASharedString);
  962.                     /*
  963.                      * Print the new string
  964.                      */
  965.                 SetCondition(((char *)Producer) + 1, 1);
  966.                     /*
  967.                      * The string has been consumed, want another one!
  968.                      */
  969.              }
  970.           }
  971.  
  972.           void Producer(void)
  973.           {
  974.              while(TRUE)
  975.              {
  976.  
  977.                WaitCondition(((char *)Producer) + 1, 1);
  978.                     /*
  979.                      * Wait for a need for a new string.
  980.                      */
  981.                gets(ASharedString);
  982.                     /*
  983.                      * Get another string.
  984.                      */
  985.                SetCondition(((char *)Producer) + 1, 1);
  986.                     /*
  987.                      * The string has been produced!
  988.                      */
  989.              }
  990.           }
  991.  
  992.      This code will work with as many Producer and Consumer threads as
  993.      you like.  Only one condition occurs, so only WaitCondition()
  994.      is satisfied for each SetCondition() call.  Also, the startup
  995.      case assumes that no string is, at first, available.  The code
  996.      would be slightly different without that assumption.
  997.  
  998.      Also notice that these programs use -odd- addresses for condition
  999.      variables.  This is to avoid potential address collisions.  Most
  1000.      lock functions occur at even addresses, so I have all of my
  1001.      programs use the odd addresses for conditional varialbes, thereby
  1002.      eliminating a potential source of confusing.  There is NO dictum
  1003.      that requires you to follow in my footsteps, though.
  1004.  
  1005.  
  1006.      Multi-Process Signalling
  1007.  
  1008.      Multi-Process Signalling allows an arbitrarily large number of
  1009.      programs to find out when something happens.  Where condition
  1010.      variables keep track of the number of conditions that occurred
  1011.      but haven't been handled, the Multi-Process signalling
  1012.      cannot keep track of the number of "outstanding" conditions
  1013.      that have occurred.  However, it can wake up EVERYONE
  1014.      waiting on the WaitLevel() macro.
  1015.  
  1016.           WaitLevel(address); waits for a condition to occur associated
  1017.                               with the passed address.
  1018.  
  1019.           SetLevel(address);  makes the multi-process signalling
  1020.                               condition occur.
  1021.  
  1022.      Examples are left as an exercise for the reader.
  1023.  
  1024.  
  1025.      Performance Issues
  1026.  
  1027.           Considering the extended number of features that SHADOW
  1028.      includes, and especially considering that the vast majority of
  1029.      semaphores are created and deleted on the fly, it may not be too
  1030.      surprising to discover that SHADOW's semaphoring system is slower
  1031.      than Exec's.  Indeed, it would be miraculous if it weren't.  What
  1032.      is surprising is that SHADOW's semaphores are very competitive with
  1033.      Exec's, and if we reduce every exec semaphore access to include a
  1034.      FindSemaphore() call (a Find() on a string that is less than four
  1035.      byteslong, even), we find that SHADOW's semaphores run at about
  1036.      the same speed as Exec's semaphores.
  1037.  
  1038.           To be more precise, SHADOW's semaphores have a performance
  1039.      overhead of about 2-5x that of Exec's local semaphoring system (that
  1040.      is, Exec's semaphores without the overhead of FindSemaphore()) and
  1041.      about the same overhead as Exec's global semaphoring system (that is,
  1042.      Exec's semaphores with the FindSemaphore() overhead).
  1043.  
  1044.           EXEC's global semaphoring system has a frightening overhead when
  1045.      either large strings (more than four characters) or large numbers of
  1046.      semaphores are used.  For instance, if each string is about sixteen
  1047.      characters long instead of four, Exec does only about half as well as
  1048.      SHADOW.  Because SHADOW uses addresses as a semaphoring point,
  1049.      instead of strings, and because SHADOW stores its semaphores on a
  1050.      binary tree, instead of a linked list, SHADOW's overhead grows
  1051.      slower than Exec's.  The test cases sited here are performed in the
  1052.      included "perftest" program, and uses only 12 semaphores at a time --
  1053.      a reasonable figure, it seems to me.
  1054.  
  1055.           But what if you want to use SHADOW's semaphores and key the
  1056.      semaphores on a string?  Use the PSemString() and VSemString() calls!
  1057.      Essentially, the string is UseString()d by the system, and the
  1058.      address of the returned string is used as the semaphoring point.
  1059.      This allows for both the flexibility of named semaphores, while
  1060.      allowing for the speed of unnamed semaphores.
  1061.  
  1062.           A good use for named semaphores is for coordinating the startup
  1063.      of a program.  Consider what happens if two copies of the same program
  1064.      get launched nearly simultaneously.  Because each program defines
  1065.      the same classes to the system (usually), it is wise to start each
  1066.      program only once, and then merely instantiate another existence
  1067.      of the program once started.  This provides the flexibility of
  1068.      "resident" programs, without the constant overhead for programs
  1069.      that aren't needed.
  1070.  
  1071.      Unfortunately, it is difficult to coordinate the simultaneous
  1072.      startup of programs.  Browser uses the following code:
  1073.  
  1074.         PSemString("Browser Program", SSEM_LOCK);
  1075.         /*
  1076.          * CreateInstance() only works if the BLOCK_CLASS is already
  1077.          * defined (which means that another browser program has been
  1078.          * launched).
  1079.          *
  1080.          * Otherwise, we're the first browser program, and will have to
  1081.          * define BLOCK_CLASS, etc.
  1082.          */
  1083.         if (CreateInstance(NULL, BLOCK_CLASS, META_CLASS, METHOD_END))
  1084.         {
  1085.            VSemString("Browser Program");
  1086.            return;
  1087.         }
  1088.         .
  1089.         . /* define BLOCK_CLASS, etc. */
  1090.         .
  1091.         VSemString("Browser Program");
  1092.         .
  1093.         .
  1094.         .
  1095.  
  1096.      Here we see that if two Browser Programs startup, the code is
  1097.      guaranteed to define the BLOCK_CLASS in one program, and then
  1098.      instantiate an instance of BLOCK_CLASS in the other.
  1099.  
  1100.      --
  1101.  
  1102.           Getting back to performance issues, another advantage to SHADOW's
  1103.      semaphores is that they take less space than their Exec counterparts,
  1104.      first because they don't require the overhead of semaphore-lists, and
  1105.      second because the semaphores don't need to exist in memory until
  1106.      needed.  It is worth noting that SHADOW will pseudo-busy wait if it
  1107.      tries to allocate a semaphore structure but runs out of memory,
  1108.      unless, of course, it was passed the SSEM_ATTEMPT flag -- in which
  1109.      case it fails.
  1110.  
  1111.           So we conclude that while Exec semaphores have some speed
  1112.      advantage in the simple cases, they are slower when sharing semaphores
  1113.      across more than one program, always take more memory, and are more
  1114.      difficult to setup and use.  I definitely recommend the SHADOW
  1115.      subsystem over the Exec subsystem, if only for the flexibility that
  1116.      it offers unless speed is absolutely crucial.
  1117.  
  1118.  
  1119.  
  1120.                                    AVLTREES
  1121.  
  1122.           AVL trees are auto-balancing binary trees.  Again, this
  1123.      discussion assumes that you have read a decent algorithms book which
  1124.      would cover AVL trees.  I suggest "Data Structgures and Program
  1125.      Design in C" by Robert L. Kruse, Bruse P. Leung, and Clovis L. Tondo.
  1126.  
  1127.           SHADOW's AVL trees are built on top of the semaphore code, the
  1128.      memory code (discussed below), the resource tracking code, and, in
  1129.      some part, the system string code.  They are designed for quick
  1130.      access by many processes and are used to store metas, classes,
  1131.      and other instances within SHADOW.  Again, the design goal of SHADOW
  1132.      is to provide for multi-threaded computing, so all AVL trees are
  1133.      semaphored when accessed by the system.
  1134.  
  1135.           To initialize an avl tree:
  1136.  
  1137.                AVLTree  bt = NULL;
  1138.  
  1139.           Now you can add any number of SHADOW-objects (the definition of
  1140.      exactly what a SHADOW-object is is located below) to the binary tree.
  1141.      Indeed, you may add any number of objects with any key to any number
  1142.      of binary trees.  AVLTrees usually require you to have different
  1143.      keys for each inserted item.  SHADOW does not impose this
  1144.      restriction.  Exec requires you to have one Node structure for
  1145.      each list that an "object" might belong to.  Again, SHADOW does
  1146.      not require this.  SHADOW allocates and frees its own BinNodes
  1147.      internally when required.  In addition, to support the idea of
  1148.      non-unique keys, the RemoveTreeNode() function takes the key AND
  1149.      the address of the object you want removed -- if you don't know the
  1150.      address of the object, you can find it by calling FindTreeNode()
  1151.      with the key.  The first object found with that key will be returned,
  1152.      and you may pass this to the RemoveTreeNode() function.
  1153.  
  1154.                OBJECT object1 = <>, object2 = <>;
  1155.  
  1156.                AddTreeNode(&bt, object1, 12);
  1157.                AddTreeNode(&bt, object2, 13);
  1158.                /*
  1159.                 * Note: object1 and object2 have not been swallowed,
  1160.                 * so swallow them now!
  1161.                 */
  1162.                DropObject(object1);
  1163.                DropObject(object2);
  1164.  
  1165.                .
  1166.                .
  1167.                .
  1168.  
  1169.                obj = FindTreeNode(&bt, 13);
  1170.                /*
  1171.                 * do something useful with obj!
  1172.                 */
  1173.                DropObject(obj);
  1174.  
  1175.                .
  1176.                .
  1177.                .
  1178.  
  1179.                /*
  1180.                 * Now we remove all of our nodes from the tree.
  1181.                 */
  1182.                obj = FindTreeNode(&bt, 12);
  1183.                RemoveTreeNode(&bt, obj, 12);
  1184.                DropObject(obj);
  1185.                obj = FindTreeNode(&bt, 13);
  1186.                RemoveTreeNode(&bt, obj, 13);
  1187.                DropObject(obj);
  1188.  
  1189.                /*
  1190.                 * Alternatively, we can call FreeTreeAllNodes()
  1191.                 * providing we meet the following specifications:
  1192.                 *
  1193.                 *   All nodes that were added must have been added to
  1194.                 *   the tree via the Add*TreeStringNode() functions.
  1195.                 *
  1196.                 *   All nodes not added this way must have keys
  1197.                 *   between 0 and 255 or keys equal to the object's
  1198.                 *   address  [ie: AddTreeNode(&bt, obj, (ULONG)obj)].
  1199.                 *
  1200.                 * FreeTreeAllNodes(&bt);
  1201.                 *
  1202.                 * This removes all nodes from the tree and drops all
  1203.                 * the applicable resources.
  1204.                 */
  1205.  
  1206.      SHADOW provides functions which sort the objects by a string.  Do
  1207.      not fool yourself into believing that this produces an object tree
  1208.      which is sorted alphabetically -- SHADOW's string routines are built
  1209.      around the system strings mentioned above.  Sorting of the nodes
  1210.      occurs according to the -address- of the system string, NOT the
  1211.      contents of the string itself.
  1212.  
  1213.                /*
  1214.                 * Add some objects keyed on some strings.
  1215.                 */
  1216.                AddTreeStringNode(&bt, object1, "Sort me!");
  1217.                AddTreeStringNode(&bt, object2, "Me too!");
  1218.                DropObject(object1);
  1219.                DropObject(object2);
  1220.  
  1221.                .
  1222.                .
  1223.                .
  1224.  
  1225.                /*
  1226.                 * We now demonstrate both lookups of objects using
  1227.                 *  a string as a key, and removal of objects from
  1228.                 *  trees where the object was keyed on a string.
  1229.                 */
  1230.                object1 = FindTreeStringNode(&bt, "Sort me!");
  1231.                object2 = FindTreeStringNode(&bt, "Me too!");
  1232.                RemoveTreeStringNode(&bt, object1, "Sort me!");
  1233.                RemoveTreeStringNode(&bt, object2, "Me too!");
  1234.                DropObject(object1);
  1235.                DropObject(object2);
  1236.  
  1237.  
  1238.      SHADOW also provides a routine which will call a function on each
  1239.      node within the bintree -- passing in the node, the key, and a
  1240.      value passed into the RecurseTree() function.
  1241.  
  1242.                /*
  1243.                 * Example RecurseTree() callback.
  1244.                 *
  1245.                 * Here we are looking for something inside an object
  1246.                 * that matches the passed "passedIn" variable.
  1247.                 *
  1248.                 * We will return the object if found, or NULL to
  1249.                 * continue looking.
  1250.                 * The Recurse() function then returns what we
  1251.                 * return, or NULL if we never return anything.
  1252.                 */
  1253.                void __regargs *myFunc(OBJECT obj, ULONG key,
  1254.                                       void *passedIn)
  1255.                {
  1256.                     struct SomePrivStruct *sps;
  1257.  
  1258.                     sps = FindAttribute(obj, ATTR_PRIVATESTUFF);
  1259.  
  1260.                     if (sps->sps_searchMe == passedIn)
  1261.                          return UseObject(obj);   /* return object used! */
  1262.  
  1263.                     return NULL;
  1264.                }
  1265.  
  1266.                .
  1267.                .
  1268.                .
  1269.  
  1270.                /*
  1271.                 * Find the object that has the correct info inside it.
  1272.                 * Search all objects on the binary tree -- we don't
  1273.                 * particularly care about the order.
  1274.                 *
  1275.                 * The number 67 is passed to the myFunc callback as
  1276.                 * the "passedIn" variable of that function.
  1277.                 */
  1278.                found = RecurseTree(&bt, myFunc, (void *)67,
  1279.                                    SHADOW_RECURSE_INORDER);
  1280.                .
  1281.                .
  1282.                .
  1283.                DropObject(found);
  1284.  
  1285.      Here we have demonstrated a search implemented via the RecurseTree().
  1286.      Using this function we can also duplicate an entire tree:
  1287.  
  1288.                /*
  1289.                 * We assume that the binary tree had all of its objects
  1290.                 * stored according to a system string object.
  1291.                 * Adjustments would have to be made if this were not so.
  1292.                 */
  1293.                void __regargs *dupStringFunc(OBJECT obj, ULONG key,
  1294.                                              AVLTree *passedIn)
  1295.                {
  1296.                     AddTreeStringNode(passedIn, obj, (char *)key);
  1297.                     /*
  1298.                      * Remember to return NULL to continue the
  1299.                      * recursion!
  1300.                      */
  1301.                     return NULL;
  1302.                }
  1303.  
  1304.                .
  1305.                .
  1306.                .
  1307.  
  1308.                /*
  1309.                 * Duplicate the avl tree.
  1310.                 * bt gets duplicated onto bt2.  Don't forget to set bt2
  1311.                 * to NULL to start with!
  1312.                 */
  1313.                bt2 = NULL;
  1314.                RecurseTree(&bt, myFunc, &bt2, SHADOW_RECURSE_INORDER);
  1315.  
  1316.                /*
  1317.                 * bt2 now has a direct copy of bt.
  1318.                 */
  1319.  
  1320.                .
  1321.                .
  1322.                .
  1323.  
  1324.                /*
  1325.                 * Cleanup!
  1326.                 */
  1327.                FreeTreeAllNodes(&bt);
  1328.                FreeTreeAllNodes(&bt2);
  1329.  
  1330.      There are several different ways to recurse down the binary tree:
  1331.                SHADOW_RECURSE_PREORDER
  1332.                SHADOW_RECURSE_INORDER
  1333.                SHADOW_RECURSE_POSTORDER
  1334.                SHADOW_RECURSE_BACKORDER
  1335.  
  1336.      PREORDER visits the nodes as Root, left-tree, right-tree.  INORDER
  1337.      visits the nodes as left-tree, Root, right-tree -- giving an
  1338.      increasing key traversal.  POSTORDER visits the nodes as left-tree,
  1339.      Root, right-tree.  BACKORDER visits the nodes as right-tree, Root,
  1340.      left-tree -- giving a decreasng key traversal.
  1341.  
  1342.      Several macro functions have been provided in much the same way that
  1343.      macros are available for many of the different PSem() semaphore calls.
  1344.      These are:
  1345.                DoPreOrderTree(&bt, func, passedIn)
  1346.                DoInOrderTree(&bt, func, passedIn)
  1347.                DoPostOrderTree(&bt, func, passedIn)
  1348.                DoBackOrderTree(&bt, func, passedIn)
  1349.  
  1350.      They directly correspond to the flags of similar name.
  1351.  
  1352.  
  1353.      There are several important things to remember when using SHADOW's
  1354.      AVL trees.  For one thing, some of the functions assume that the
  1355.      nodes are sorted by system string address (FreeTreeAllNodes()),
  1356.      and won't be happy if they find out otherwise.  Other functions,
  1357.      like RecurseTree() don't care, but the callback functions supplied
  1358.      to this call might.
  1359.  
  1360.      In addition, it is important to remember that all nodes are sorted
  1361.      on unsigned keys, not signed keys.  Also, all objects added to avl
  1362.      trees must be SHADOW objects, not some concoction of your own.
  1363.      This either means that the first three longwords correspond to the
  1364.      struct ClasslessObject definition, or that the object was
  1365.      created via a CreateInstance()/CreateSubClass() or equivalent call.
  1366.  
  1367.  
  1368.      Performance Issues
  1369.  
  1370.      SHADOW's AVL tree functions (apart from the *Watched*() functions
  1371.      which are really a part of another subsystem) are implemented in
  1372.      assembly.  They are guaranteed to use less than 256 bytes of stack,
  1373.      independent of how large the tree grows -- assuming the tree resides
  1374.      in a 32bit address space, that is.
  1375.  
  1376.      Despite the relatively large overhead implied by the use of SHADOW's
  1377.      semaphores, a node can be found in a 256 node tree in an average of
  1378.      84 microseconds on my A3000 -- an object stored with a string as
  1379.      the key would take slightly longer, depending on the length of the
  1380.      string.
  1381.  
  1382.      Adding and removing nodes takes slightly longer.  Again, with an
  1383.      average of 256 nodes inserted and removed in order, the total
  1384.      add-remove cycle takes about 280 microseconds (on the A3000).
  1385.      This is not bad, considering that the binary nodes used internally
  1386.      by the AVLTree subsystem are allocated and freed as needed.  Exec
  1387.      lists, while they do not have this assorted cost, are less flexible
  1388.      about how many times an "object" can be added and to how many trees.
  1389.  
  1390.      You should pick the storage structure that is best suited for you.
  1391.      Fortunately, SHADOW now gives you a choice, without forcing you to
  1392.      do your own implementation.
  1393.  
  1394.  
  1395.      Summary
  1396.  
  1397.      In summary, there are only four really important AVLTREE calls to
  1398.      understand:
  1399.           AddTreeNode()    adds an object to an AVL tree sorted by a
  1400.                             passed key.
  1401.           RemoveTreeNode() removes an object from the AVL tree.
  1402.           FindTreeNode()   finds the object in an AVL tree, and returns
  1403.                             this object.  You should DropObject() that
  1404.                             object when you are done using it, as is TRUE
  1405.                             of any resource that SHADOW returns to you.
  1406.           RecurseTree()    calls a function for every object in the binary
  1407.                             tree.
  1408.  
  1409.      The other function which is NOT a derivative of these four is
  1410.      FreeTreeAllNodes().  It's a pretty esoteric call -- it just removes
  1411.      all objects from the AVL tree, though.  Nothing special.  [Note
  1412.      the restrictions on its use!]
  1413.  
  1414.      All other AVL tree calls are derivatives of the first three calls,
  1415.      and four other calls [DoPreOrderTree() for example] are built
  1416.      directly on top of RecurseTree() as #defines macros.
  1417.  
  1418.  
  1419.                                     SLISTS
  1420.  
  1421.           SLISTs are single-linked, prioritized lists.  The salient
  1422.      functions are:
  1423.           AddSListNode()
  1424.           RemoveSListNode()
  1425.           FindNodePriInSList()
  1426.  
  1427.      Like their SHADOW avltree cousins, these functions are semaphored and
  1428.      the objects added into the lists are resource-tracked -- so all
  1429.      objects added to these lists MUST be SHADOW-objects.
  1430.  
  1431.      The only advantages they add over Exec is the resource tracking, the
  1432.      automatic semaphores and the ability for objects to reside in
  1433.      multiple lists, multiple numbers of times.  The real disadvantage is
  1434.      that there is a great deal of overhead for adding prioritized
  1435.      nodes, and removing nodes.
  1436.  
  1437.      Use them if you find them useful.
  1438.  
  1439.  
  1440.  
  1441.                                     MEMORY
  1442.  
  1443.           In this discussion about semaphores and AVLTrees and SLists, we
  1444.      have glossed over one consistent point -- memory management.  For
  1445.      instance, we know that SHADOW's semaphores have some kind of
  1446.      internally managed semaphore object, we know that AVLTrees have some
  1447.      kind of internally managed BinNode structures, and we know that
  1448.      SLISTs have some kind of internally managed Node structures, but we
  1449.      don't know how these structures are allocated and freed.
  1450.  
  1451.      SHADOW has a memory subsystem that is responsible for dealing with
  1452.      these tasks.  While its main purpose is to create and destroy these
  1453.      small structures as fast as possible, it has enough hooks to be
  1454.      useful to anyone who has to manage a large number of structures of
  1455.      exactly the same size.
  1456.  
  1457.      The following are the functions which implement SHADOW's MEMORY
  1458.      subsystem:
  1459.           result = (BOOL)InitTable(SEMLIST list, allocfunc, freefunc, size)
  1460.           memory = (void *)AllocateItem(SEMLIST list)
  1461.           (void)FreeItem(SEMLIST list, (void *)memory)
  1462.           FreeTable(SEMLIST list)
  1463.  
  1464.  
  1465.      You use these memory tables by first calling the InitTable(),
  1466.      using the AllocateItem() and FreeItem() calls, and then calling
  1467.      FreeTable().
  1468.  
  1469.           struct MemoryList globalMemList;
  1470.  
  1471.           InitTable(&globalMemList, NULL, NULL, 16);
  1472.  
  1473.      This initializes the memory  table to use the default allocation and
  1474.      free calls (AllocMem(MEMF_PUBLIC)/FreeMem()).  In addition, it sets
  1475.      up the memory table to dole out memory in 16byte chunks, and
  1476.      pre-allocates 32 items.
  1477.  
  1478.      Each item allocated from the table will be 16bytes long.  The contents
  1479.      of the items are not guaranteed to be initialized to zero, EVEN if you
  1480.      use your own function callbacks to allocate the memory as MEMF_CLEAR.
  1481.      Items are allocated from Exec's memory pools 32chunks at a time.
  1482.      When enough items are put back into the table, the table will free
  1483.      a full 32chunks back into Exec's free memory pool.  The definition
  1484.      of "enough" is purposely left vague.
  1485.  
  1486.           memory = AllocateItem(&globalMemList);
  1487.           FreeItem(&globalMemList, memory);
  1488.  
  1489.      When all allocations are finished and you are done using the memory
  1490.      list, call FreeTable().  This frees all the still allocated items
  1491.      that you might have left allocated, and it returns all resources
  1492.      to Exec's free memory pool.
  1493.  
  1494.           memory = AllocateItem(&globalMemList);
  1495.           .
  1496.           .
  1497.           .
  1498.           FreeTable(&globalMemList);    /* All outstanding allocations are
  1499.                                            returned to Exec */
  1500.  
  1501.  
  1502.      Here are the default allocmem() and freemem() callbacks that SHADOW
  1503.      uses to allocate and free memory from Exec.  These functions are
  1504.      called everytime a new chunk of 32items is needed from Exec.
  1505.  
  1506.           void * __regargs temporaryAlloc(struct MemoryList *list)
  1507.           {
  1508.              return AllocMem(32 * list->memlst_size +
  1509.                               sizeof(struct MemoryNode),
  1510.                              MEMF_PUBLIC);
  1511.           }
  1512.  
  1513.           void __regargs temporaryFree(struct MemoryList *list,
  1514.                                        struct MemoryNode *node)
  1515.           {
  1516.                FreeMem(node, 32 * list->memlst_size +
  1517.                              sizeof(struct MemoryNode));
  1518.           }
  1519.  
  1520.  
  1521.      Again, because SHADOW is principly designed for multi-threaded work,
  1522.      these memory lists use semaphores for each AllocateItem() and
  1523.      FreeItem() call.  If you don't need this protection, set the
  1524.      memlst_semUse to FALSE, and the EXEC Signal Semaphore that is a part
  1525.      of the MemoryList structure will not be used.  [You should set
  1526.      the semUse flag AFTER the InitTable() is called, and not before!]
  1527.  
  1528.      The relative performance of SHADOW is hard to measure because Exec's
  1529.      memory allocation and freeing routines can differ by a factor of
  1530.      five.  However, SHADOW's memory allocation routines, even
  1531.      WITH the semaphore, are about as fast as I've ever seen Exec's
  1532.      routines.  Without the semaphore they are obviously faster.
  1533.  
  1534.      What is plainly (painfully!) obvious is how bad Exec's routines
  1535.      degenerate when frees are perfectly interleaved (that is, when
  1536.      all of the odd numbered allocations are freed first, and then
  1537.      all the even numbered allocations are freed).  SHADOW is
  1538.      faster in this case by, at least, a factor of ten!
  1539.  
  1540.      Obviously, if the semaphore is not needed, SHADOW's memory routines
  1541.      are much faster than Exec's memory routines.  No attempt has been
  1542.      made to compare SHADOW's routines with anything other than Exec's
  1543.      AllocMem() and FreeMem() calls.  It is possible that some of Exec's
  1544.      other routines would be faster (in certain cases) than SHADOW's.
  1545.      It is up to the programmer to decide which set of functions best
  1546.      suits his or her purpose.
  1547.  
  1548.  
  1549.  
  1550.                                      OOPL
  1551.  
  1552.      Now that you know a little about the functional interface to the
  1553.      library, it is high time to introduce the object-oriented interface.
  1554.      However, this seems to be a good time to mention what SHADOW is NOT.
  1555.      SHADOW is not an Object-Oriented Programming Language (OOPL).  It is
  1556.      also not a Concurrent-Object-Oriented Programming Language.  Indeed,
  1557.      it is not a language at all.  It is entirely possible to envision a
  1558.      language built around SHADOW at some point in the future.  For right
  1559.      now, however, SHADOW is accessed via the 'C' language.
  1560.  
  1561.      So why not have built around an existing object-oriented language?
  1562.      True, the addition of semaphores, avl-trees, system strings, and
  1563.      even (with a bit of work) resource tracking, would all be possible
  1564.      under a language such as C++.  However, rewriting C++'s method
  1565.      dispatcher to provide for the synchronous and asynchronous method
  1566.      sends would have proved difficult, as C++ doesn't have one.
  1567.  
  1568.      Indeed, the languages that do exist which have concurrency syntax
  1569.      are usually built not only as "concurrent" languages, but as
  1570.      "distributed, concurrent" languages.  This type of abstraction
  1571.      is not necessary on the Amiga as the Amiga has only a single
  1572.      "process-space".  And yes, this implies you can't magically
  1573.      hook up two Amigas with SHADOW and expect things to work
  1574.      twice as fast together (or together at all at whatever speed).
  1575.      Remember, the design goal of SHADOW was to take advantage of
  1576.      the Amiga's better facilities -- principally among them, low
  1577.      overhead task switching and shared memory, not to add
  1578.      functionality.
  1579.  
  1580.  
  1581.                                 Object Oriented
  1582.  
  1583.      So what is object-oriented programming anyway?  The classical
  1584.      definition of object-oriented programming is by example --
  1585.      ie: object-oriented programming was introduced to the world as a
  1586.      brand new way of programming, but the only formal definition
  1587.      offered was the example language implementations at the time
  1588.      (Smalltalk being the better known of these).  This is an unacceptable
  1589.      definition.  Unfortunately, the term has grown to mean a number
  1590.      of things to a number of people.
  1591.  
  1592.      However, there are some general "philosophies" behind Object-Oriented
  1593.      Programming.  Object-Oriented Programming attempts to model the
  1594.      world as a collection of "things" where these "things" have
  1595.      certain "characteristics" and are capable of performing certain
  1596.      "actions".  For instance, dogs are things that have certain hair-
  1597.      color, height, weight, etc.  Dogs can also do things like "bark",
  1598.      "eat", "sleep", etc.
  1599.  
  1600.      In addition to this, many object-oriented languages draw the
  1601.      distinction between definitive "things" and indefinite "things".
  1602.      For example, "-a- dog" is an example of an indefinite thing,
  1603.      whereas "-the- dog Rover" is an example of a definitive thing.
  1604.      As SHADOW makes this distinction this is not of purely
  1605.      academic interest.
  1606.  
  1607.      Object-Oriented Programming offers some names for the above, however
  1608.      while many of the names are consistent, some of them are not.  We
  1609.      will use the terminology that SHADOW uses.  if you wish to
  1610.      understand SHADOW against the wider diversity of object-oriented
  1611.      systems, you'll have to do some more reading.
  1612.  
  1613.      Under SHADOW, "things" are referred to as objects.  Often you will
  1614.      see the term in uppercase as in "OBJECTs" -- this is because
  1615.      "OBJECT" is an actual 'C' defined type (typedef), there is no
  1616.      difference here.  Some later distinction is drawn between "Object"
  1617.      and "object," but that is immaterial to this discussion.  Under
  1618.      SHADOW, indefinite things are objects, definite things are objects,
  1619.      everything is an object.
  1620.  
  1621.      An indefinite object is referred to as a class (or, alternatively in
  1622.      some of the docs, as a "cob_class").  A definite object is called
  1623.      an instance.  A definite object is always an instance of a particular
  1624.      corresponding indefinite object.  For example, "Rover" is an
  1625.      instance of "dog":
  1626.  
  1627.  
  1628.                       Real World   Object World   Description
  1629.                       ----------   ------------   -----------------
  1630.                          DOG    -- class       -- indefinite object
  1631.                          Rover  -- instance    -- definite object
  1632.  
  1633.  
  1634.      In addition, these object have physical "characteristics" and they
  1635.      possess the ability to perform "actions".  Under SHADOW, these
  1636.      charateristics are referred to as attributes, and the actions are
  1637.      referred to as methods.  In addition, attributes and methods are
  1638.      both -described- in a class, not in an instance.  For example,
  1639.      when we model a dog in the computer system, we define certain
  1640.      characteristics and actions that dogs, in general, will have
  1641.      and perform.  A dog has a certain hair color, weight, height,
  1642.      and they all know how to bark, eat, and sleep.  Rover, as an
  1643.      instance of this imaginary dog-class, automatically would contain
  1644.      the space for its characteristics, and could call the "bark",
  1645.      "eat", and "sleep" routines that were specified in the class.
  1646.  
  1647.      We can imagine some simple code that illustrates this:
  1648.  
  1649.           new CLASS called DOG {
  1650.                Attributes:
  1651.                   weight is a float
  1652.                   height is a float
  1653.                   hair-color is a set containing (black, brown, white)
  1654.  
  1655.                Methods:
  1656.                   bark:
  1657.                      play_sound "dh0:samples/barkingDog"
  1658.                      end
  1659.                   eat:
  1660.                      say "Munch, munch, slurp, slurp, munch, munch."
  1661.                      end
  1662.                   sleep:
  1663.                      display "dh0:pictures/SleepingDog.pic"
  1664.                      end
  1665.           }
  1666.  
  1667.           new INSTANCE called ROVER of CLASS DOG {
  1668.                weight = 13.2
  1669.                height = 33.6
  1670.                hair-color is (brown, black)
  1671.           }
  1672.  
  1673.      At this point you can do something like:
  1674.           ROVER bark
  1675.  
  1676.      and ROVER would 'play_sound "dh0:samples/barkingDog"'.
  1677.  
  1678.      Obviously, while the syntax is different, there hasn't been much
  1679.      gained over straight 'C' here.  But we aren't quite finished yet!
  1680.  
  1681.      There is yet one more feature to Object-Oriented Programming, and
  1682.      that is the notion of "subclassing".  Subclassing is the heart
  1683.      of "reuse' -- the notion that old code never dies, it just gets
  1684.      recycled.  Let's take the same dog example again.  Dogs are nice,
  1685.      for some people, but others prefer a particular -subclass- of
  1686.      dogs, say, hunting dogs.  A hunting dogs do useful things like
  1687.      retrieving dead ducks from slimey swamp pits.  So, we have the
  1688.      following pseudocode:
  1689.  
  1690.           new CLASS called HUNTING_DOG as SUBCLASS of DOG {
  1691.                methods:
  1692.                   retrieve:
  1693.                      exists duck?
  1694.                         print "Dog now has duck"
  1695.                      else
  1696.                         object bark
  1697.                      end
  1698.           }
  1699.  
  1700.           new INSTANCE called SPOT of class HUNTING_DOG {
  1701.                weight = 15.2
  1702.                height = 30,5
  1703.                hair-color is (black, white)
  1704.           }
  1705.  
  1706.      Notice that the HUNTING_DOG instance SPOT can specify all the
  1707.      attributes that ROVER did.  This is because HUNTING_DOG "inherits"
  1708.      the attribute definitions from its "superclass", DOG.
  1709.  
  1710.      In addition, if there was no duck, and you did the following:
  1711.  
  1712.           SPOT retrieve
  1713.  
  1714.      SPOT would bark (else...  object bark)!  Just as HUNTING_DOG
  1715.      inherits attributes, it inherits the methods of DOG as well.
  1716.  
  1717.      There is one more possibility:
  1718.  
  1719.           ROVER retrieve
  1720.  
  1721.      Remember, ROVER is not a HUNTING_DOG.  ROVER, therefore, does not
  1722.      know how to retrieve   Under SHADOW, nothing happens (an error
  1723.      code is set in your process object, though).  In C++ this causes
  1724.      a compile-time error, and in Objective-C it causes a program
  1725.      failure ("Unimplemented Method").  SHADOW is a bit more forgiving
  1726.      than either of these languages....
  1727.  
  1728.  
  1729.                   Object-Orientedness under SHADOW
  1730.  
  1731.      That taken care of, you can imagine that creating class descriptions,
  1732.      in particular the descriptions of attributes and methods of a new
  1733.      class, in 'C' can be quite challenging.  And I'm afraid that you
  1734.      would be correct in that assessment.
  1735.  
  1736.      The rest of this document focuses on the issue of creating new
  1737.      classes and describes the inter-relationships of the classes
  1738.      provided to you by SHADOW.
  1739.  
  1740.      The document 'ShadowLibraryMethods.doc' introduces SHADOW's
  1741.      object-oriented system.  Excepting some brief reiterations
  1742.      and clarifications, that document will continue to contain
  1743.      the main introduction to the object-oriented system.  Therefore,
  1744.      it is not merely highly suggested that you read the introduction
  1745.      in ShadowLibraryMethods.doc, it is prerequisite that you do so.
  1746.  
  1747.      To reiterate, aside from the functional aspects of SHADOW which
  1748.      are introduced above, everything under SHADOW is an object.  All
  1749.      SHADOW objects begin with a struct CoreObject which can be found in
  1750.      <shadow/core.h>.  There are two fields in this structure, the
  1751.      cob_useCount and the cob_class.  The cob_useCount is used by the
  1752.      resource tracking system and is covered earlier in this document.
  1753.      The cob_class points to another OBJECT which contains the
  1754.      information that is necessary to create, destroy, and otherwise
  1755.      maintain this object.  In short, this description (or blueprint or
  1756.      class), contains a description of the attributes (or properties) of
  1757.      the object and the methods (or functions, or verbs) of the object.
  1758.  
  1759.      The relationship between "object" and "cob_class" is called the
  1760.      "instantiation hierarchy".  Object-oriented systems typically
  1761.      describe classes in relation to other classes.  The former group
  1762.      of classes are called subclasses, and the latter are called
  1763.      superclasses.
  1764.  
  1765.      In the case of the "superclass hierarchy", there is a class which
  1766.      has no superclass.  This class is typically referred to as the
  1767.      rootclass.  In the case of SHADOW's "instantiation hierarchy", there
  1768.      is no object without a cob_class, after all, an object without
  1769.      a description is pretty hard to create!  However, there is a
  1770.      descriptor which, instead of terminating the instantiation
  1771.      hierarchy, is a descriptor for itself.  These descriptions
  1772.      are referred to as METAs.
  1773.  
  1774.      The instantiation hierarchy for SHADOW is three deep.  For a summary,
  1775.      please refer to the "instantiate hierarchy" entry in the Glossary.doc.
  1776.  
  1777.      This three-level setup allows you, the programmer, to change
  1778.      the methods of both your objects (by changing the method
  1779.      descriptions in your classes) -and- your classes (by
  1780.      changing the method descriptions in your metas).
  1781.  
  1782.      There is more about METAs in ShadowLibraryMethods.doc, and I suggest
  1783.      you read about them there as well.  This document is more concerned
  1784.      with how you actually create classes and metas, rather than what
  1785.      classes and metas -are-.
  1786.  
  1787.  
  1788.                               Creating Classes
  1789.  
  1790.      Classes contain two important descriptions -- attribute descriptions
  1791.      and method descriptions.  Attribute descriptions are very
  1792.      straightforward.  Method descriptions can become very complicated.
  1793.  
  1794.      Classes are created by specifying the new class name, the superclass,
  1795.      the new methods, and the new attributes like so:
  1796.  
  1797.          CreateSubClass(NULL, ROOT_CLASS, META_CLASS,
  1798.                                           MY_CLASS_NAME,
  1799.                                           NULL,
  1800.                                           myNewAttrs,
  1801.                                           myNewMethods,
  1802.                                           METHOD_END);
  1803.  
  1804.      AttributeTags
  1805.  
  1806.      The attribute description is a NULL-terminated array of ATTRIBUTE_TAGs.
  1807.      Each element contains the following information:
  1808.           typedef struct AttributeTag {
  1809.              char          *attag_name;
  1810.              ULONG          attag_size;
  1811.              void          *attag_default;
  1812.           } ATTRIBUTE_TAG;
  1813.  
  1814.      The attag_name is the name of the attribute.  When you have an instance
  1815.      of your created class you can access this attribute inside of the
  1816.      instance by calling the FindAttribute() function passing in the
  1817.      attag_name of the attribute you want access to.
  1818.  
  1819.      The attag_size is the size of the particular attribute you want
  1820.      created.
  1821.  
  1822.      The attag_default is a pointer to a structure containing the
  1823.      default values of the attribute.  If this is not specified, the
  1824.      default value for the entire attribute-structure will be zeroes.
  1825.  
  1826.      Consider the following example.  Say you wanted to create an object
  1827.      that described a car.  You want to keep certain information
  1828.      about this car inside of an attribute.
  1829.  
  1830.      First thing is to create a C structure:
  1831.           struct CarInfo {
  1832.                ULONG     ci_wheelSize;
  1833.                ULONG     ci_steeringSide;
  1834.                .
  1835.                .
  1836.                .
  1837.           };
  1838.      In this example, the ci_wheelSize refers to the size of the wheels
  1839.      on the car and the ci_steeringSide refers to the side of the
  1840.      car that the steering wheel is on, and so forth.
  1841.  
  1842.      The next thing to do is to pick a name for the attribute.  Please
  1843.      note that the name is case-sensitive!:
  1844.  
  1845.           #define ATTR_MY_CAR_INFO "My information about this car"
  1846.  
  1847.      We can now create the ATTRIBUTE_TAG structure:
  1848.  
  1849.           ATTRIBUTE_TAG myNewAttrs[] = {
  1850.                {
  1851.                     ATTR_MY_CAR_INFO,
  1852.                     sizeof(struct CarInfo),
  1853.                     NULL
  1854.                },
  1855.                TAG_END
  1856.           };
  1857.  
  1858.      If we had wanted to we could have created default values for the
  1859.      attributes so that when the class' instances are created, they
  1860.      automatically have some non-zero values in them:
  1861.  
  1862.           struct CarInfo defaultCarInfo = {14, LEFT, ...};
  1863.  
  1864.      In which case the ATTRIBUTE_TAG structure becomes:
  1865.  
  1866.           ATTRIBUTE_TAG myNewAttrs[] = {
  1867.                {
  1868.                     ATTR_MY_CAR_INFO,
  1869.                     sizeof(struct CarInfo),
  1870.                     &defaultCarInfo
  1871.                },
  1872.                TAG_END
  1873.           };
  1874.  
  1875.  
  1876.      The creation of the class would necessarily follow this.  Note that
  1877.      this code makes the class a resource that will be freed when the
  1878.      process defining the resource exits:
  1879.  
  1880.           AddAutoResource(NULL, CreateSubClass(NULL,
  1881.                                                ROOT_CLASS,
  1882.                                                META_CLASS,
  1883.                                                CAR_CLASS,
  1884.                                                NULL,
  1885.                                                myNewAttrs,
  1886.                                                METHOD_END),
  1887.                                 CAR_CLASS);
  1888.  
  1889.      Once we had this class, we can create an instance of the class:
  1890.  
  1891.           automobile = CreateInstance(NULL, CAR_CLASS, META_CLASS);
  1892.  
  1893.      So, now that an actual car instance exists, you can access the car's
  1894.      instance variables (the attributes of the instance), with the
  1895.      following code:
  1896.  
  1897.           {
  1898.              struct CarInfo *ci;
  1899.  
  1900.              ci = FindAttribute(automobile, ATTR_MY_CAR_INFO);
  1901.  
  1902.              .
  1903.              .
  1904.              .
  1905.  
  1906.           }
  1907.  
  1908.      Once you have a pointer to the object's instance variables, you can
  1909.      manipulate the structure, change variables, etc.  However, this
  1910.      pointer will only remain valid as long as the object it is assoicated
  1911.      with remains valid.  In other words, once you
  1912.      "DropObject(automobile)", you may not use the "ci" variable again.
  1913.      After all, the object may have been destroyed!
  1914.  
  1915.      In addition, if this structure is to be shared across many processes,
  1916.      you will want to protect access via the semaphore system (described
  1917.      above).  Fo all of my code I use the "ci" pointer to semaphore the
  1918.      entire structure.  You may find a more suitable way of doing the
  1919.      same thing, if you wish.  At anyrate, your code might look like:
  1920.  
  1921.           {
  1922.              struct CarInfo *ci;
  1923.  
  1924.              ci = FindAttribute(automobile, ATTR_MY_CAR_INFO);
  1925.              PSem(ci, SSEM_READ);
  1926.  
  1927.              .
  1928.              . // Various reads on the structure taking place.
  1929.              .
  1930.  
  1931.              VSem(ci);
  1932.           }
  1933.  
  1934.  
  1935.  
  1936.      The next step involves showing off a shortcut in SHADOW.  SHADOW's
  1937.      attributes, as defined by both META_CLASS and META_CLUSTER, are
  1938.      created in the order that they are specified.  This means that if
  1939.      you create two attributes, one after the other, a pointer to
  1940.      the first structure will easily lead you to a pointer to the second.
  1941.  
  1942.  
  1943.           struct CarInfoExternal {
  1944.                ULONG     cie_wheelSize;
  1945.                ULONG     cie_wheelBase;
  1946.                .
  1947.                .
  1948.                .
  1949.           };
  1950.           #define ATTR_CAR_INFO_EXT "Information about the car's externals"
  1951.  
  1952.           struct CarInfoInternal {
  1953.                ULONG     cii_headClearance;
  1954.                ULONG     cii_cubicCapacity;
  1955.           .
  1956.           .
  1957.           .
  1958.           };
  1959.           #define ATTR_CAR_INFO_INT "Information about the car's internals"
  1960.  
  1961.           ATTRIBUTE_TAG myNewAttrs[] = {
  1962.                {
  1963.                     ATTR_CAR_INFO_EXT,
  1964.                     sizeof(struct CarInfoExternal),
  1965.                     NULL
  1966.                },
  1967.                {
  1968.                     ATTR_CAR_INFO_INT,
  1969.                     sizeof(struct CarInfoInternal,
  1970.                     NULL
  1971.                },
  1972.                TAG_END
  1973.           };
  1974.  
  1975.           .
  1976.           .
  1977.           .
  1978.  
  1979.           AddAutoResource(NULL, CreateSubClass(NULL,
  1980.                                                ROOT_CLASS,
  1981.                                                META_CLASS,
  1982.                                                CAR_CLASS,
  1983.                                                NULL,
  1984.                                                myNewAttrs,
  1985.                                                METHOD_END),
  1986.                                 CAR_CLASS);
  1987.  
  1988.           automobile = CreateInstance(NULL, CAR_CLASS, META_CLASS);
  1989.  
  1990.      Assuming the above, the following:
  1991.  
  1992.           {
  1993.              struct CarInfoExt *ce;
  1994.  
  1995.              ce = FindAttribute(automobile, ATTR_MY_CAR_INFO);
  1996.  
  1997.              .
  1998.              .
  1999.              .
  2000.  
  2001.           }
  2002.  
  2003.      is identical to this:
  2004.           {
  2005.              struct CarInfo *ci;
  2006.  
  2007.              ci = FindAttribute(automobile, ATTR_MY_CAR_INFO);
  2008.  
  2009.              .
  2010.              .
  2011.              .
  2012.  
  2013.           }
  2014.  
  2015.      where "struct CarInfo" is declared as follows:
  2016.           struct CarInfo {
  2017.                struct CorInfoExt   ci_cie;
  2018.                struct CarInfoInt   ci_cii;
  2019.           }
  2020.  
  2021.      As is apparent from this, a pointer to the first structure may very
  2022.      well be useful to getting at other data.  The advantages of having
  2023.      separate attributes in the first place is to save space when using
  2024.      explicitly declared default attributes, or if you wish to offer
  2025.      the ability for subclasses to alter the default values for some
  2026.      subset of your attributes without affecting some other subsets
  2027.      of your attributes.
  2028.  
  2029.  
  2030.      The above is an example of a time optimization.  Instead of looking
  2031.      up both attributes separately, only one was looked up, and the
  2032.      second attribute's address was able to be hard-coded.
  2033.  
  2034.      A similar improvement is possible when looking up attributes
  2035.      frequently within methods.  You can lock a global pointer to a class
  2036.      and then lookup the "struct Attribute" which contains the necessary
  2037.      information to later find the attribute locations within any instance
  2038.      of that class.  Note that you will have to keep that global
  2039.      pointer "locked" in memory for the entire time that the Attribute
  2040.      structure is being used.
  2041.  
  2042.           DropObject(SetObject(&globalClass, FindShadowClass(CAR_CLASS)));
  2043.           /*
  2044.            * Note that FindShadowClass() returns a used class here.
  2045.            *
  2046.            * We use SetObject() to avoid race-conditions if
  2047.            * more than one task tries to set the variable at
  2048.            * the same time.
  2049.            */
  2050.  
  2051.           struct Attribute *globalAttr = FindAttrDefn(globalClass,
  2052.                                                       ATTR_INFO);
  2053.  
  2054.           /*
  2055.            *
  2056.            * We should not DropObject(globalClass) until we no longer
  2057.            * need the attribute.
  2058.            */
  2059.           .
  2060.           .
  2061.           .
  2062.  
  2063.           Now, say you have some instance 'object', and it is the correct
  2064.      class (object->cob_class = globalClass or somes subclass of
  2065.      globalClass).  Now you may merely add the value in the attr_offset
  2066.      field within the attribute structure to the instance address and
  2067.      arrive at the attribute within the object:
  2068.  
  2069.           .
  2070.           .
  2071.           .
  2072.           {
  2073.                struct Info *info = (void *)(((ULONG)object) +
  2074.                                             attr->attr_offset);
  2075.                .
  2076.                .
  2077.                .
  2078.           }
  2079.           .
  2080.           .
  2081.           .
  2082.  
  2083.  
  2084.      This will occur far faster than the FindAttribute() call.  The two
  2085.      disadvantages are the initial startup costs and shutdown costs, as
  2086.      well as the necessity to keep the class "locked" in memory for the
  2087.      entire time you have a globalAttr pointer.
  2088.  
  2089.           .
  2090.           .
  2091.           .
  2092.           /*
  2093.            * CLEANUP!
  2094.            *
  2095.            * As we no longer need the globalAttr, we clear the globalClass
  2096.            */
  2097.           DropObject(SetObject(&globalObject, NULL));
  2098.  
  2099.  
  2100.      The only remaining thing to note about attributes is this: once the
  2101.      class is created, the AttributeTag structures -and- the default
  2102.      attribute structures are no longer needed by the system.  The
  2103.      system copies everything into memory when the class is created, so it
  2104.      is very possible to have the AttributeTags and the default values
  2105.      reside in a text file and load the text file at runtime, and then
  2106.      purge the text file.
  2107.  
  2108.      This means that changing the default attribute structure after the
  2109.      class is created will NOT change the creation of future objects.
  2110.      You can change these default attributes pretty easily, however.  Here
  2111.      is some example code.  It takes a longword value within the default
  2112.      object and updates it according to a hex value:
  2113.  
  2114.           {
  2115.              ULONG *ptr = GetDefaultAttribute(myClass, ATTR_WEBPAUSE, 0);
  2116.  
  2117.              /*
  2118.               * ptr may or may not exist!  If ptr did not exist, then
  2119.               * the attribute did not originally have a default
  2120.               * attribute and the machine ran out of memory trying to
  2121.               * allocate one, or the attribute itself did not
  2122.               * exist within the passed class.
  2123.               */
  2124.              if (ptr)
  2125.                 /*
  2126.                  * Convert the hex-string in args[0] and save the number
  2127.                  * into the longword pointer.
  2128.                  */
  2129.                 stch_l(args[0], ptr);
  2130.           }
  2131.  
  2132.      GetDefaultAttribute() is defined as follows:
  2133.  
  2134.           /*
  2135.            * Looks for a default attribute object for the name'd
  2136.            * definition.
  2137.            *
  2138.            * Returns either NULL (failure) or the default object + offset.
  2139.            *
  2140.            * Will create a default object if it doesn't exist.
  2141.            * Will create a NEW default object if the superclass has the
  2142.            *  same default object.  Note, subclasses -share- the same
  2143.            *  default attribute object with their superclasses
  2144.            *  [or, rather, vice-versa].
  2145.            *
  2146.            * Watched attributes do not -have- default values.
  2147.            */
  2148.           void *GetDefaultAttribute(CLASS class, char *name, ULONG offset)
  2149.           {
  2150.              struct Attribute *attr = FindAttrDefn(class, name);
  2151.              struct Attribute *temp;
  2152.              struct ClasslessObject *obj;
  2153.  
  2154.              if (!attr || (attr->attr_size & FLAG_ATTR_WATCHED))
  2155.                 return NULL;
  2156.  
  2157.              temp = FindAttrDefn(class->ccl_superClass, name);
  2158.              if (!(obj = attr->attr_value) ||
  2159.                  (temp && obj == temp->attr_value))
  2160.              {
  2161.                 if (obj)
  2162.                 {
  2163.                    /*
  2164.                     * Create a new object for the subclass and copy
  2165.                     * in the values from the superclass' default
  2166.                     * object.
  2167.                     */
  2168.                    if (!(obj = CreateObject(obj + 1, attr->attr_size)))
  2169.                       return NULL;
  2170.                 } else
  2171.                 {
  2172.                    /*
  2173.                     * If we have no default object, then the default
  2174.                     * values are all zero.
  2175.                     */
  2176.                    if (!(obj = AllocMem(sizeof(struct ClasslessObject) +
  2177.                                            attr->attr_size,
  2178.                                         MEMF_PUBLIC | MEMF_CLEAR)))
  2179.                    {
  2180.                       return NULL;
  2181.                    }
  2182.                    /*
  2183.                     * Setup the new object with a size and usecount
  2184.                     */
  2185.                    obj->clb_size = sizeof(struct ClasslessObject) +
  2186.                                         attr->attr_size;
  2187.                    obj->clb_useCount = 1;
  2188.  
  2189.                 }
  2190.                 /*
  2191.                  * Save the new default object into the class --
  2192.                  *  drop any old default objects there, and make sure
  2193.                  *  the operation is single-threaded.
  2194.                  */
  2195.                 DropObject(SetObject(&attr->attr_value, obj));
  2196.              }
  2197.  
  2198.              /*
  2199.               * Returns a pointer into the default object so that you
  2200.               * cn directly dork with the values.
  2201.               */
  2202.              return (void *)(((ULONG)(obj + 1)) + offset);
  2203.           }
  2204.  
  2205.  
  2206.      MethodTags
  2207.  
  2208.      As mentioned above, attribute creation is fairly straightforward --
  2209.      when compared to method creation.  And that's because creating a
  2210.      method involves a lot more than just creating a couple of structures.
  2211.      It involves considering the types of methods you have available --
  2212.      synchronous, asynchronous, call-by-port, call-by-process.  It also
  2213.      involves creating the actual functions and function argument tags,
  2214.      and the process classes (and instances) that you might need to run the
  2215.      method inside of.
  2216.  
  2217.      This presents the creation of several types of methods from the
  2218.      function-level up.  However, you should realize that the process is
  2219.      not always so straightforward.
  2220.  
  2221.      A method's function always has at least the following four arguments:
  2222.           struct IPCMessage *msg;
  2223.           OBJECT            object;
  2224.           META              class;
  2225.           char              *MethodID;
  2226.  
  2227.      The <msg> parameter is used by the asynchronous method sends.  It
  2228.      is provided for handling some strange resource-tracking cases that
  2229.      are discussed below.  This parameter is often NULL.
  2230.  
  2231.      The <object> parameter is the object on which the selector is being
  2232.      invoked.  This parameter is never NULL.
  2233.  
  2234.      The <class> parameter is a pointer to the class of the object that
  2235.      actually handles the given MethodID.  Remember, every object is an
  2236.      instance of some class.  Classes live in a class-hierarchy from which
  2237.      they inherit other class' methods.  Therefore, when a particular
  2238.      method is invoked, the method itself might be handled by the object's
  2239.      class, or some superclass of the object's class.  This parameter
  2240.      distinguishes -which- class handles the passed selector....
  2241.  
  2242.      The <MethodID> parameter is a pointer to a system-string which
  2243.      corresponds to the selector for this particular method.  It is a
  2244.      useful way to allow multiply different methods to converge on one
  2245.      of your functions, and then diverge when the method is sent off
  2246.      to the superclass for further handling.
  2247.  
  2248.      Obviously, typing all of this information into each function would
  2249.      be tedious.  The includes defines the macro "METHOD_ARGS", which
  2250.      contains these argument definitions.  The ability of your compiler to
  2251.      handle this shortcut may differ from mine....
  2252.  
  2253.      Here is a sample method-function definition:
  2254.  
  2255.           void ProcTestMethod(METHOD_ARGS)
  2256.           {
  2257.              BPTR oldoutput;
  2258.  
  2259.              oldoutput = SelectOutput(Open("CONSOLE:", MODE_OLDFILE));
  2260.  
  2261.              VPrintf("Called method: <%s> ", (ULONG *)&MethodID);
  2262.              VPrintf("in process <%s>\n", (ULONG *)&FindTask(NULL)->
  2263.                                                     tc_Node.ln_Name);
  2264.              VPrintf("\tin Class <%s> ", (ULONG *)&(CurrentProcess()->
  2265.                                                     cob_class->meta_name));
  2266.              VPrintf(((msg)?"Asynchronously\n":"Synchronously\n"), NULL);
  2267.  
  2268.              oldoutput = SelectOutput(oldoutput);
  2269.              Close(oldoutput);
  2270.  
  2271.              CallSuper();
  2272.           }
  2273.  
  2274.      This is a relatively simple method-function that the included perftest
  2275.      program uses to verify nearly all of the method-invoke possiblities
  2276.      (by function, synchronous msg, asynchronous msg, etc).
  2277.  
  2278.  
  2279.      Once you have this function written, you need to create an
  2280.      ARGUMENT_TAG structure which describes the addition arguments that
  2281.      the method tags (above the four described above).  Because this
  2282.      method does not take any extra arguments, we will postpone talking
  2283.      about ARGUMENT_TAGs until we have a more appropriate example.
  2284.  
  2285.      Now we have to create a name for the selector of this method:
  2286.  
  2287.           #define METH_MY_NEW_PROC_TESTER "A string of your own choosing"
  2288.  
  2289.      The next thing to do is to add the method into your class' METHOD_TAG
  2290.      list.  The METHOD_TAG list has eight different variables.  We take
  2291.      each in turn.
  2292.  
  2293.      The first field in the METHOD_TAG structure is the mtag_MethodID
  2294.      field.  This field is very straightforward -- it should always
  2295.      carry a non-NULL field that is a pointer to the string which is
  2296.      to represent the method's selector.
  2297.  
  2298.      The next two fields are named: mtag_procObject and mtag_defnObject.
  2299.      Although they have similar names, their meanings are very different.
  2300.      The mtag_defnObject is used by the system to ensure that the program
  2301.      (or some other loaded code, like a library) which contains the
  2302.      definition of the function and the ARGUMENT_TAG in its seglist,
  2303.      does NOT disappear until all reference to the function by any
  2304.      methods in any classes are eliminated.
  2305.  
  2306.  
  2307.      mtag_defnObject:
  2308.  
  2309.      Here's the basic problem.  Program A has loaded a method, M.  Program
  2310.      A now defines a public class C which has the method M.
  2311.      Program B now comes along, creates an instance I of class C.  Program
  2312.      A quits.  Program B tries to invoke method M of instance I, which used
  2313.      to reside in Program A, but has been since eliminated.  This is
  2314.      clearly not good.  The same thing might happen for a library base
  2315.      (although the OpenCnt of the library base should be enough to
  2316.      catch all legal errors in this respect).
  2317.  
  2318.      What we do in the instance of a library is to define a global,
  2319.      classless object, and use the pointer to this global object as the
  2320.      defn_object:
  2321.                     struct ClasslessObject
  2322.                               globalLibraryObject = {0, 0, 0};
  2323.  
  2324.      In the EXPUNGE code, you'll want to verify that the
  2325.      globalLibraryObject.cob_useCount is zero.  If it is a non-zero
  2326.      integer, then someone still has a pointer to an object that has
  2327.      a class (or some superclass thereof) that points to a method in
  2328.      your library.  Thus, your method may still be called, and you
  2329.      should refuse to expunge your library.
  2330.  
  2331.      This is a distinct advantage over BOOPSI where class libraries are
  2332.      difficult to manage because of the various resource tracking issues.
  2333.  
  2334.  
  2335.      However, creating methods inside of libraries is not likely to be
  2336.      the most frequent thing you do.  Far more frequently you will be
  2337.      creating classes inside of programs.  And for this, you will need to
  2338.      retrieve the CurrentProcess() -- a pointer to your process' object
  2339.      that was created during the InitOOProgram() call (the FIRST function
  2340.      you should always call after your OpenLibrary("shadow.library", 5) is
  2341.      InitOOProgram()!!! -- see the Process Class discussion below).
  2342.  
  2343.           /*
  2344.            * CurrentProcess() is a macro defined in <shadow/shadowProto.h>
  2345.            *
  2346.            * This object is not resource tracked, so you need not
  2347.            *  DropObject() this processObject after you're done using it.
  2348.            */
  2349.           processObject = CurrentProcess();
  2350.  
  2351.      Once you have this pointer, you modify each of your MethodTags to
  2352.      have the mtag_defnObject point to the CurrentProcess() object.
  2353.      This is tedious, and the funcion SetupMethodTags() is provided to
  2354.      help you in this respect.  We will talk more about SetupMethodTags
  2355.      when we begin the discussion of mtag_procObject.
  2356.  
  2357.      It stands to reason, as well, that you need to be in the process
  2358.      that the program runs in!  In otherwords, FindTask(NULL) had
  2359.      better be your program's task, or you are going to end up with the
  2360.      wrong process!
  2361.  
  2362.      At anyrate, once your class is created, your program cannot exit
  2363.      before the methods are no longer callable via this class because the
  2364.      last call your program should make before the CloseLibrary(ShadowBase)
  2365.      is the RemoveCurrentProgram() call, which will not return until all
  2366.      references to your CurrentProcess() go away.  Again, you have a safe
  2367.      way in which to define public classes and ensure no one has a pointer
  2368.      to your internal methods when your program ends up quitting and
  2369.      returning its resources to the system.
  2370.  
  2371.  
  2372.      mtag_procObject:
  2373.  
  2374.      The mtag_procObject is used to help determine what process the
  2375.      object's method needs to be run in.  SHADOW is very flexible in its
  2376.      ability to run methods in other processes.  You can ask for something
  2377.      as simple as a function call, or something much more complicated,
  2378.      like "invoke a method by creating a process of the given named class;
  2379.      then, replace the named class with a pointer to the actual class
  2380.      pointer so that the next invocation no longer has to look the class
  2381.      up in the class list."
  2382.  
  2383.      While the mtag_defnObject is always a SHADOW object, the
  2384.      mtag_procObject can be any number of things.  Some of the mtag_flags
  2385.      are used to decide what the mtag_procObject is pointing to.
  2386.  
  2387.      A valid mtag_procObject is one of five values:
  2388.           a) it is a pointer to a process object.
  2389.           b) it is a pointer to an IPCPort.
  2390.           c) it is a pointer to a process class
  2391.           d) it is a pointer to a struct MethInvokeSpec <shadow/method.h>
  2392.           e) it is NULL
  2393.  
  2394.  
  2395.      mtag_flags:
  2396.  
  2397.      There are five flags in the mtag_flags which control the behaviour of
  2398.      these various possibilities:
  2399.  
  2400.           METH_FLAG_OBJECT  - The mtag_procObject is interpreted as the
  2401.                                destination process object.
  2402.           METH_FLAG_PORT    - The mtag_procObject is interpreted as an
  2403.                                IPCPort to which method-messages are sent.
  2404.           METH_FLAG_CLASS   - The mtag_procObject is interprted as a
  2405.                                class of process objects.  This class is
  2406.                                instantiated, and the resulting process
  2407.                                object is used as the destination for
  2408.                                that particular method invocation.
  2409.  
  2410.           METH_FLAG_OBJECT_AS_DEST
  2411.                             - The object the method is being invoked on is
  2412.                                itself used as the destination process
  2413.                                object.  This is particularly useful for
  2414.                                process methods which must (for reasons of
  2415.                                port allocation or other signal-bit
  2416.                                allocation, etc.) be run in the process
  2417.                                with which the process-object corresponds.
  2418.  
  2419.           METH_FLAG_SPEC    - The mtag_procObject is interpreted as a
  2420.                                pointer to a struct MethInvokeSpec
  2421.                                [see: <shadow/method.h>], the complete
  2422.                                meaning of which depends on the above four
  2423.                                flags in the following manner:
  2424.  
  2425.              METH_FLAG_SPEC | METH_FLAG_OBJECT
  2426.                 mis_instanceName is the name of the process object to use
  2427.                                   as the destination object.
  2428.                 mis_className    is the class name of the class of the
  2429.                                   above process object.
  2430.                 mis_metaName     is the meta's name of the meta of the
  2431.                                   above class.  Usually this is
  2432.                                   META_CLASS.
  2433.  
  2434.              METH_FLAG_SPEC | METH_FLAG_PORT
  2435.                 mis_instanceName is the name of the IPCPort to use as the
  2436.                                   destination port.  This port will NOT
  2437.                                   be created, but must exist when the
  2438.                                   method is called.
  2439.                 mis_className    is NULL
  2440.                 mis_metaName     is NULL
  2441.  
  2442.              METH_FLAG_SPEC | METH_FLAG_CLASS
  2443.                 mis_instanceName is NULL
  2444.                 mis_className    is the class name of the process to
  2445.                                   create.
  2446.                 mis_metName      is the meta's name of the meta of the
  2447.                                   above class.  Usually this is META_CLASS.
  2448.  
  2449.              METH_FLAG_SPEC | METH_FLAG_OBJECT_AS_DEST
  2450.                 the METH_FLAG_SPEC is meaningless in this case as the
  2451.                 METH_FLAG_OBJECT_AS_DEST never uses the mtag_procObject
  2452.                 field.
  2453.  
  2454.  
  2455.                                When the process/port is looked-up/created,
  2456.                                the process/port may replace the
  2457.                                MethInvokeSpec structure, eliminating the
  2458.                                need to lookup the object/class/port again.
  2459.                                To do this, set the SPEC_SAVE_BINDING flag
  2460.                                in the mis_flags field of the MethInvokeSpec
  2461.                                structure.
  2462.  
  2463.  
  2464.      A NULL mtag_procObject is treated differently depending on the
  2465.      mtag_flags and the mtag_threadstat.  As the mtag_threadstat hasn't
  2466.      been discussed yet, though, it makes sense to do that first.
  2467.  
  2468.  
  2469.      mtag_threadstat:
  2470.  
  2471.      Where the mtag_flags field describes how the mtag_procObject is
  2472.      interpreted during a method call, the mtag_threadstat determines how
  2473.      the method is actually invoked.  The following flags are defined in
  2474.      <shadow/message.h>:
  2475.  
  2476.          INVOKE_CALL       - Calls method as function.
  2477.  
  2478.          INVOKE_SYNC       - The method is invoked as a synchronous message
  2479.                               to the method's mtag_procObject, unless the
  2480.                               calling process belongs to a subclass
  2481.                               of the destination's process' class,
  2482.                               in which case the method is called as a
  2483.                               function.
  2484.  
  2485.          INVOKE_ASYNC      - The method is invoked as an asynchronous
  2486.                               message to the method's mtag_procObject,
  2487.                               unless the calling process belongs to a
  2488.                               subclass of the destination's process'
  2489.                               class, in which case the method is called
  2490.                               as a function.
  2491.  
  2492.          INVOKE_FORCE      - Used in onjunction with above two flags.  See
  2493.                               next two flags.
  2494.  
  2495.          INVOKE_FORCE_SYNC - The method is sent as a synchronous message
  2496.                               unless the calling process is exactly
  2497.                               equivalent to the destination process, in
  2498.                               which case the method is called as a funcion.
  2499.  
  2500.          INVOKE_FORCE_ASYNC- The method is ALWAYS sent as an asynchronous
  2501.                               message.  Irregardless of calling/destination
  2502.                               process.
  2503.  
  2504.          INVOKE_IGNORE_PROCESS
  2505.                            - It is possible for programers to call DSM()
  2506.                               and its associated helper functions
  2507.                               (eg. DoShadowInProcess()) to override the
  2508.                               process that the method is handled in.
  2509.                               Setting this flag in the mtag_threadstat
  2510.                               prevents the mtag_procObject from being
  2511.                               overridden with the INVOKE_WITH_PROCESS
  2512.                               flag that can be sent to DSM().  See the
  2513.                               Autodoc for DSM() for more information.
  2514.  
  2515.  
  2516.      As you can see, there are a variety of ways in which to call a method.
  2517.      You can call a method asynchronously to a named IPCPort (mtag_flags
  2518.      would be METH_FLAG_PORT | METH_FLAG_SPEC, mtag_threadstat would be
  2519.      INVOKE_FORCE_ASYNC, and mtag_procObject would be a pointer to a
  2520.      MethInvokeSpec structure) or invoke a method as a simple function
  2521.      call (mtag_flags would be METH_FLAG_OBJECT, mtag_threadstat would be
  2522.      INVOKE_CALL, mtag_procObject would be NULL).  Many of the callable
  2523.      attributes are overridable at method invoke-time, so you can
  2524.      determine how the method is invoked both in the method definition
  2525.      (which is being discssed here) and method invocation (which will be
  2526.      described briefly here, and is described more fully in the DSM()
  2527.      autodoc).
  2528.  
  2529.      When mtag_procObject is NULL, there are several possible reactions by
  2530.      SHADOW, depending (as mentioned above) on the mtag_flags and
  2531.      mtag_threadstat settings:
  2532.  
  2533.           INVOKE_CALL         - NULL mtag_procObject is ignored, call
  2534.                                  proceeds as usual.
  2535.  
  2536.        mtag_flags = METH_FLAG_OBJECT:
  2537.  
  2538.           INVOKE_[FORCE]_SYNC - Method is only successfully called if the
  2539.                                  calling task has no associated SHADOW
  2540.                                  object.  Otherwise DSM() or its helper
  2541.                                  functions (eg. DoShadow()) returns NULL.
  2542.                                  If the calling task has no associated
  2543.                                  SHADOW object, the method is invoked as
  2544.                                  a function call.
  2545.  
  2546.           INVOKE_[FORCE]_ASYNC- Similar logic to the INVOKE_SYNC logic
  2547.                                  above except that INVOKE_FORCE_ASYNC where
  2548.                                  the mtag_procObject is NULL will force an
  2549.                                  asynchronous method-invoke message to be
  2550.                                  sent to the calling task's SHADOW-method
  2551.                                  port, stored in the sp_port field of the
  2552.                                  struct ShadowProcess structure in the
  2553.                                  ATTR_SHADOWPROCESS attribute of the
  2554.                                  process' object.  If there is no current
  2555.                                  task, the message should be called as a
  2556.                                  function.
  2557.  
  2558.  
  2559.        mtag_flags = METH_FLAG_PORT
  2560.                -or-
  2561.        mtag_flags = METH_FLAG_CLASS
  2562.  
  2563.           A NULL port/class (mtag_procObject), except when mtag_threadstat
  2564.           is INVOKE_CALL, will cause the method invocation to fail.  DSM(),
  2565.           or its helpers (eg. DoShadow()), will return NULL.
  2566.  
  2567.  
  2568.        mtag_flags = METH_FLAG_OBJECT_AS_DEST
  2569.  
  2570.           A NULL mtag_procObject is meaningless as the object argument of
  2571.           the DoShadow() call (or the first pointer in the arguments
  2572.           array to DSM()) is used as the process object to which to send
  2573.           the method.
  2574.  
  2575.  
  2576.        mtag_flags | METH_FLAG_SPEC
  2577.  
  2578.           Not specifying a pointer to a MethInvokeSpec structure in the
  2579.           mtag_procObject when the flag METH_FLAG_SPEC is set in the
  2580.           mtag_flags field is a programming error.  Your class will not
  2581.           be created under these conditions.  mtag_procObject MUST be
  2582.           non-NULL, and had BETTER point to a MethInvokeSpec structure.
  2583.           This MethInvokeSpec structure is copied when the class is
  2584.           created, so you need not have your MethInvokeSpec structures
  2585.           lying around after your classes are created.
  2586.  
  2587.      mtag_priority
  2588.           This field is only used by the PATCHER_CLASS.  It is ignored
  2589.           by the class, though you set it to zero for future
  2590.           compatibility.
  2591.  
  2592.      mtag_method
  2593.           This should contain a pointer to the function which implements
  2594.           the method.
  2595.  
  2596.      mtag_arguments
  2597.           This should point to an ARGUMENT_TAG array that describes the
  2598.           arguments the method's function takes (this allows asynchronous
  2599.           methods to have their arguments correctly resource-tracked),
  2600.           and describes the return value of the function.  If the function
  2601.           does not return anything, and there are no arguments above and
  2602.           beyond the METHOD_ARGS default arguments of all methods, then
  2603.           this field may be NULL.  The pointer to this supplied
  2604.           ARGUMENT_TAG is -not- copied, although the contents of the
  2605.           ARGUMENT_TAG MUST remain constant.  The ARGUMENT_TAG should
  2606.           be considered a part of the method's function, rather than as
  2607.           an extension to the data that describes the method (METHOD_TAG).
  2608.           That data (the METHOD_TAG) is copied and altered appropriately
  2609.           when the class is created.
  2610.  
  2611.  
  2612.      Given this information, here is an example METHOD_TAG which enables
  2613.      the associated class to invoke the METH_MY_NEW_PROC_TESTER method
  2614.      and have this translated into a function call to the ProcTestMethod()
  2615.      function which was described several pages ago:
  2616.  
  2617.           METHOD_TAG myNewMethods[] = {
  2618.                                         .
  2619.                                         .
  2620.                                         .
  2621.                                         <other methods for this class>
  2622.                                         .
  2623.                                         .
  2624.                                         .
  2625.                                         {
  2626.                                            METH_MY_NEW_PROC_TESTER,
  2627.                                            NULL, NULL,
  2628.                                            INVOKE_CALL,
  2629.                                            METH_FLAG_OBJECT, 0,
  2630.                                            (METHODFUNCTYPE)ProcTestMethod,
  2631.                                            NULL
  2632.                                         },
  2633.                                         TAG_END
  2634.                                       };
  2635.  
  2636.      Notice that this method tag does not specify an mtag_procObject nor
  2637.      an mtag_defnObject.  For this particular METHOD_TAG item, the
  2638.      mtag_defnObject needs to be set to the CurrentProcess() and the
  2639.      mtag_procObject can remain NULL.  However, it is quite possible that
  2640.      other METHOD_TAG items need to have their mtag_procObject be
  2641.      something other than NULL, and it is perfectly fine to specify
  2642.      an mtag_procObject as any process object you wish when the
  2643.      mtag_threadstat is INVOKE_CALL and the METH_FLAG_OBJECT is set.
  2644.  
  2645.      As the CurrentProcess() is only available at runtime, you can either
  2646.      iterate through the METHOD_TAG array and setup the defnObject and
  2647.      the procObject as you wish, or you can use the supplied
  2648.      SetupMethodTags() routine.
  2649.  
  2650.      SetupMethodTags takes three arguments -- the METHOD_TAG array, the
  2651.      procObject to be used in each element of the array, and the defnObject
  2652.      to be used in each element of the array.  If either of these last two
  2653.      arguments are specified as "-1", then the results of CurrentProcess()
  2654.      are used.  If the item already has a defnObject or procObject
  2655.      specified (non-NULL), then that field remains untouched.  This is
  2656.      especially useful when using the MethInvokeSpec structure as the
  2657.      mtag_procObject -- this is a static structure which can be set at
  2658.      compile-time....
  2659.  
  2660.      Please note!  When setting the elements of this array, SetupMethodTags
  2661.      does not UseObject() any of the objects.  Therefore, be careful if
  2662.      you ever define a new type of MetaClass which allows for classes to
  2663.      be defined asynchronously to the caller, as the mtag_procObjects and
  2664.      the mtag_defObjects will not be correctly resource-tracked....  This
  2665.      is not a problem for the default CreateSubClass() call which 99% of
  2666.      you will be using.
  2667.  
  2668.      The corresponding SetupMethodTags() for this METHOD_TAG structure
  2669.      looks as follows:
  2670.  
  2671.           /*
  2672.            * Set all procObjects and defnObjects to CurrentProcess();
  2673.            */
  2674.           SetupMethodTags(myNewMethods, (void *)-1, (void *)-1);
  2675.  
  2676.      The creation of the class would then follow this.  Note that this
  2677.      code makes the class a resource that will be freed when the
  2678.      process defining the resource exits -- also, we use the "myNewAttrs"
  2679.      ATTRIBUTE_TAG which is defined several pages back:
  2680.  
  2681.           AddAutoResource(NULL, CreateSubClass(NULL,
  2682.                                                PROCESS_CLASS,
  2683.                                                META_CLASS,
  2684.                                                MY_PROC_CLASS,
  2685.                                                NULL,
  2686.                                                myNewAttrs,
  2687.                                                myNewMethods,
  2688.                                                METHOD_END),
  2689.                                 MY_PROC_CLASS);
  2690.  
  2691.  
  2692.      We now possess a new MY_PROC_CLASS with some attributes and some methods.
  2693.      Of course, to call a method, we first need an instance!
  2694.  
  2695.           process = CreateInstance(NULL, MY_CAR_CLASS, META_CLASS);
  2696.  
  2697.      And now to call the new method:
  2698.  
  2699.           DoShadow(process, NULL, METH_MY_NEW_PROC_TESTER, METHOD_END);
  2700.  
  2701.           .
  2702.           .
  2703.           .
  2704.           /*
  2705.            * Don't forget your cleanup!
  2706.            */
  2707.           RemoveObject(process);
  2708.  
  2709.  
  2710.      The DoShadow() call is very straightforward -- first the object, then
  2711.      the class at which to begin looking for the method (NULL signifies
  2712.      object->cob_class, which is the way you'll call 80-90% of all your
  2713.      methods), then the selector string, then any arguments that are
  2714.      expected by the method, and then a METHOD_END terminator.
  2715.  
  2716.      A few things need to be pointed out here.  First, you need not
  2717.      specify all your arguments before the METHOD_END.  Arguments
  2718.      which are not specified are set to zero by the method calling code.
  2719.      This allows you to create new versions of your software, adding new
  2720.      arguments to old methods, without having to worry about anybody
  2721.      else's additions to your classes!  Provided, of course, that NULLs
  2722.      default to the old version's behaviour....
  2723.  
  2724.      Second, not specifying the METHOD_END terminator is a bug in your
  2725.      code -- don't do it, even if it does work....
  2726.  
  2727.      Third, it is VERY IMPORTANT that no argument that you pass into the
  2728.      method has the same value as METHOD_END -- 0x80000001 -- or,
  2729.      alternatively, that no two adjacent WORD arguments, combined, form a
  2730.      0x80000001.  This value was picked so as to be a very high negative
  2731.      number, an odd number (impossible address), and just plain unlikely
  2732.      to cause problems.  You will have to become clever if this is not the
  2733.      case for you....  Someday, a language might hide this detail from you,
  2734.      but you'll have to live with it for now.  If this is just not
  2735.      satisfactory, then let me suggest an alternative.  For all those
  2736.      items that require resource tracking -- strings, objects, structure
  2737.      pointers, pass those as separate arguments, and for all flags and
  2738.      numbers, etc., pass those arguments in a single method-argument as a
  2739.      TagArray structure pointer.  There is a 'TAGL' type of argument that
  2740.      will allow you to do just such a thing....
  2741.  
  2742.      Fourth, the CreateSubClass() and the CreateInstance() both have a
  2743.      METHOD_END terminator, and it now becomes clearer why -- they
  2744.      both cleverly use the passed stack arguments as arguments into a
  2745.      method call which creates either an instance of a class or a
  2746.      class itself....
  2747.  
  2748.  
  2749.      There is no reason why more than one method can call the same
  2750.      function.  The following is perfectly reasonable:
  2751.  
  2752.           #define METH_MY_NEW_PROC_TESTER_1 \
  2753.                                    "A string of your own choosing 1"
  2754.           #define METH_MY_NEW_PROC_TESTER_2 \
  2755.                                    "A string of your own choosing 2"
  2756.           METHOD_TAG myNewMethods[] = {
  2757.                                         {
  2758.                                            METH_MY_NEW_PROC_TESTER_1,
  2759.                                            NULL, NULL,
  2760.                                            INVOKE_FORCE_ASYNC,
  2761.                                            METH_FLAG_OBJECT, 0,
  2762.                                            (METHODFUNCTYPE)ProcTestMethod,
  2763.                                            NULL
  2764.                                         },
  2765.                                         {
  2766.                                            METH_MY_NEW_PROC_TESTER_2,
  2767.                                            NULL, NULL,
  2768.                                            INVOKE_SYNC,
  2769.                                            METH_FLAG_PORT, 0,
  2770.                                            (METHODFUNCTYPE)ProcTestMethod,
  2771.                                            NULL
  2772.                                         },
  2773.                                         TAG_END
  2774.                                       };
  2775.  
  2776.           .
  2777.           .
  2778.           .
  2779.  
  2780.           extern struct IPCPort *somePortSomeplace;
  2781.  
  2782.           /*
  2783.            * Set all procObjects and defnObjects to CurrentProcess();
  2784.            */
  2785.           SetupMethodTags(myNewMethods, (void *)-1, (void *)-1);
  2786.           /*
  2787.            * oops, but reset the port.
  2788.            *
  2789.            * We could have the line below and the line above switch
  2790.            * places as well!
  2791.            */
  2792.           myNewMethods[1].mtag_procObject = somePortSomeplace;
  2793.  
  2794.           /*
  2795.            * Create the class.
  2796.            */
  2797.           AddAutoResource(NULL, CreateSubClass(NULL,
  2798.                                                PROCESS_CLASS,
  2799.                                                META_CLASS,
  2800.                                                MY_PROC_CLASS,
  2801.                                                NULL,
  2802.                                                myNewAttrs,
  2803.                                                myNewMethods,
  2804.                                                METHOD_END),
  2805.                                 MY_PROC_CLASS);
  2806.  
  2807.           AddAutoResource(NULL, process = UseObject(
  2808.                                    CreateInstance(NULL,
  2809.                                                   MY_CAR_CLASS,
  2810.                                                   META_CLASS)),
  2811.                           NULL);
  2812.  
  2813.  
  2814.           /*
  2815.            * Send off some methods....
  2816.            *
  2817.            * The first is called asynchronously.
  2818.            * The second is called synchronously and is handled by the
  2819.            *  "somePortSomeplace" IPCPort.  The same function
  2820.            *  (ProcTestMethod()) should be called regardless of
  2821.            *  this, however.
  2822.            */
  2823.           DoShadow(process, NULL, METH_MY_NEW_PROC_TESTER_1, METHOD_END);
  2824.           DoShadow(process, NULL, METH_MY_NEW_PROC_TESTER_2, METHOD_END);
  2825.  
  2826.           .
  2827.           .
  2828.           .
  2829.           /*
  2830.            * Hey, don't forget the cleanup!
  2831.            */
  2832.           DropObject(process);     /*
  2833.                                     * Matches my UseObject()
  2834.                                     *  above
  2835.                                     */
  2836.  
  2837.           /*
  2838.            * Oh, and I haven't shown shutdown recently....
  2839.            */
  2840.           RemoveCurrentProgram(NULL);   /* removes the process and the
  2841.                                            process' class as they were added
  2842.                                            as autoresources.
  2843.                                          */
  2844.           CloseLibrary(ShadowBase);
  2845.           .
  2846.           .
  2847.           .
  2848.  
  2849.  
  2850.      ArgumentTags
  2851.  
  2852.      The remaining mystical portion to methods (if you've been following
  2853.      everything else I've written) are the ARGUMENT_TAGs.
  2854.  
  2855.      ARGUMENT_TAGs describe the arguments that a method receives, above
  2856.      and beyond the nominal METHOD_ARGS, which ALL methods receive.
  2857.      While it is only the number and size of the arguments which
  2858.      interests the INVOKE_CALL and INVOKE_SYNC types of calls, the type
  2859.      of the argument is very important when invoking a method
  2860.      ASYNChronously -- this is how the arguments get properly resource-
  2861.      tracked across asynchronous tasks!  In addition, if resources are
  2862.      returned from an asynchronous invocation, they need to be thrown
  2863.      away -- the ARGUMENT_TAG provides for this behaviour as well.
  2864.  
  2865.      An ARGUMENT_TAG, therefore, comes in two parts -- the first part
  2866.      describes each of the arguments -- the type, the size that is
  2867.      required for them on the stack (yes, you can pass a structure
  2868.      to a method by-value), and any additional information that
  2869.      might be needed (the size of a pointer, the size of each element
  2870.      in an array, etc.).  The second part serves as both the ARGUMENT_TAG
  2871.      terminator (the TAG_END) and contains the information about the
  2872.      returned resource (the type and the additional information fields,
  2873.      the size on the stack is not applicable as DSM() only returns
  2874.      a single long-word value in d0).
  2875.  
  2876.      The following is taken from the AutoDoc for SetMethodArgs(), a
  2877.      function which you will not need to use in all likelihood, but,
  2878.      nevertheless, the place where information about ARGUMENT_TAGs
  2879.      was previously kept.  Note that the definition for the AttributeTag
  2880.      structure resides in <shadow/misc.h>:
  2881.  
  2882.       If the method-function returns a variable that needs to be kept
  2883.       track of, the last ArgumentTag structure (whose at_tag should be
  2884.       zero) in the array should contain information for async. method
  2885.       sends to safely deallocate or otherwise resource track the
  2886.       returned value.
  2887.  
  2888.       Valid returns are:
  2889.        SHADOW_RETURN_BLANK   0
  2890.        SHADOW_RETURN_OBJECT  1
  2891.        SHADOW_RETURN_STRING  2
  2892.        SHADOW_RETURN_PORT    3
  2893.        SHADOW_RETURN_TAGL    4
  2894.        SHADOW_RETURN_PTR     5
  2895.        SHADOW_RETURN_MRFO    6
  2896.  
  2897.       These values should be stored in the at_size field of the last
  2898.       ArgumentTag array item..
  2899.  
  2900.       For SHADOW_RETURN_PTR, the size of the pointer should be stored
  2901.       in the at_flags field of the same item, for SHADOW_RETURN_TAGL,
  2902.       the size of each array item should be stored in the at_flags field
  2903.       of this last item, and for SHADOW_RETURN_MRFO, the offset of the
  2904.       returned data from the object in question should be placed in the
  2905.       at_flags field
  2906.  
  2907.       Normally, that is, in every case except the last item, the system
  2908.       supports a number of at_tag values:
  2909.          'MRFO':
  2910.             used for object values which add at_flags to
  2911.                   the passed object pointer.
  2912.             at_size should be four
  2913.             at_flags -- the offset of the passed data from the object ptr.
  2914.  
  2915.             Note that MRFO exists for system use, however you are free to
  2916.             use the item as well -- but it's of little to no use, just
  2917.             pass the object instead!
  2918.  
  2919.          'JOBJ':
  2920.             Used for an object.
  2921.             at_size should be four
  2922.             at_flags should be one of:
  2923.                SHADOW_OBJECT
  2924.                SHADOW_CLASSLESSOBJECT
  2925.                SHADOW_META
  2926.                SHADOW_CLASS,
  2927.                SHADOW_CLUSTER
  2928.                SHADOW_COMPOSITE
  2929.              though this is more for peace of mind than necessity.
  2930.  
  2931.          'JMSG':
  2932.             Used for a message creted by MessageMaker()
  2933.             at_size should be four
  2934.             at_flags should be zero
  2935.  
  2936.          'JSTR':
  2937.             Used for a system string.
  2938.             at_size should be four
  2939.             at_flags should be zero
  2940.  
  2941.          'PORT':
  2942.             Used for a ppipc port.
  2943.             at_size should be four
  2944.             at_flags should be zero
  2945.  
  2946.          'TAGL':
  2947.             Used for a NULL terminated array.
  2948.             at_size should be four.
  2949.             at_flags should be the size of each array item
  2950.  
  2951.          'RTRN':
  2952.             reserved by the system, don't use.
  2953.          'APTR'
  2954.             a pointer to some memory.
  2955.             at_size should be four.
  2956.             at_flags should be zero if you don't want to copy the data to
  2957.              another block when sending async., or should be the size of
  2958.              the data pointed to if you do.
  2959.  
  2960.          Create you own 4 character constant:
  2961.             at_size should be an even number -- you can pass arbitrarily
  2962.              large structures on the stack, this way.
  2963.             at_flags ignored.
  2964.  
  2965.  
  2966.      If we examine closely, a few limitations crop up.  For one thing,
  2967.      there doesn't seem to be a way to add another "type" of argument that
  2968.      would require a different kind of resource-tracking.  This is TRUE --
  2969.      please send me your requests!
  2970.  
  2971.      Second, you can't pass a structure pointer that point to a structure
  2972.      of more than 65535 bytes (at_flags is a UWORD value).  If you need to
  2973.      pass a 64k or bigger structure, feel free to prepend the three
  2974.      long-word values which would be required for a struct
  2975.      ClasslessObject, set the classless object's size to the final size of
  2976.      the larger structure, and pass the large structure as an object.
  2977.      The structure will thus not be copied between asynchronous tasks
  2978.      (saving time and space).  If you need to copy the object, use the
  2979.      original structure (without the struct ClasslessObject header) in a
  2980.      call to CreateObject() like this:
  2981.  
  2982.           newObject = CreateObject(bigStructurePointer,
  2983.                                    sizeof(BigStructure));
  2984.  
  2985.      This will create a new ClasslessObject structure with the values found
  2986.      in the passed bigStructurePointer copied into the returned object.
  2987.      The method call would proceed:
  2988.  
  2989.           DoShadowAsync(object, NULL, SOME_METHOD, newObject, METHOD_END);
  2990.           /*
  2991.            * No Longer need this!
  2992.            */
  2993.           DropObject(newObject);
  2994.  
  2995.      Similarly, the array item in a TAGL can not be larger than 65k.  The
  2996.      array can be larger, just not each individual item....
  2997.  
  2998.  
  2999.      The following example introduces ARGUMENT_TAGs in a real-world
  3000.      application.  The code is analogous to the METH_INIT code for the
  3001.      ROOT_CLASS.  This method (as the browser indicates) takes a single
  3002.      parameter -- a 'JSTR'.  This string is used to add the object to
  3003.      some kind of global (avl) tree structure.  If the string is NULL,
  3004.      then the object's address is used instead.  For the ROOT_CLASS'
  3005.      METH_INIT, the global tree ends up being the object's class'
  3006.      ATTR_INSTANCETREE tree, not a global tree as in this example:
  3007.  
  3008.           METHOD_TAG carMethods[] = {
  3009.                                         .
  3010.                                         .
  3011.                                         .
  3012.                                         {
  3013.                                            METH_CAR_REGISTER
  3014.                                            NULL, NULL,
  3015.                                            SHADOW_MSG_CALL,
  3016.                                            METHOD_FLAG_PROC, 0,
  3017.                                            RegisterCarMethod,
  3018.                                               REF_RegisterCarMethod
  3019.                                         },
  3020.                                         TAG_END
  3021.                                      };
  3022.  
  3023.  
  3024.           .
  3025.           .
  3026.           .
  3027.           /*
  3028.            * The MY_CAR_CLASS methods.
  3029.            *
  3030.            * METH_CAR_REGISTER:
  3031.            */
  3032.           ARGUMENT_TAG  REF_RegisterCarMethod[] =
  3033.                            {
  3034.                               {'JSTR', 4, 0}, /* The one argument */
  3035.                               {TAG_END, SHADOW_RETURN_OBJECT, 0}
  3036.                                               /* The return spec. */
  3037.                            };
  3038.           OBJECT RegisterCarMethod(METHOD_ARGS, char *string)
  3039.           {
  3040.              /*
  3041.               * This method "swallows" the object passed in and
  3042.               * either returns or removes it.  Therefore, we must
  3043.               * transfer the object out of any asynchronous messages
  3044.               * we might receive.
  3045.               *
  3046.               * The "0" is used because "object" is the "zeroth"
  3047.               * argument to this method.  "class" is first, "MethodID"
  3048.               * is second, "string" is third, etc.
  3049.               */
  3050.  
  3051.              IPCARGTransfer(msg, 0);
  3052.  
  3053.              /*
  3054.               * If there is a name, add the object to the tree with that
  3055.               * name, otherwise add the object using the object's address.
  3056.               */
  3057.              if ((!string && AddWatchedTreeNode(globalTree
  3058.                                                 object,
  3059.                                                 (long)object)) ||
  3060.                  (string && AddWatchedTreeStringNode(globalTree,
  3061.                                                      object,
  3062.                                                      string)))
  3063.              {
  3064.                 return object;
  3065.              }
  3066.  
  3067.              /*
  3068.               * Failure case, cleanup!
  3069.               */
  3070.              RemoveObject(object);
  3071.              return NULL;
  3072.           }
  3073.  
  3074.           .
  3075.           .
  3076.           .
  3077.  
  3078.           /*
  3079.            * Creating the MY_ROOT_CLASS
  3080.            */
  3081.           SetupMethodTags(rootMethods, (void *)-1, (void *)-1);
  3082.           myRoot = CreateSubClass(NULL, ROOT_CLASS, META_CLASS,
  3083.                                                     MY_CAR_CLASS,
  3084.                                                     NULL,
  3085.                                                     NULL,
  3086.                                                       /* No new attrs */
  3087.                                                     rootMethods,
  3088.                                                     METHOD_END);
  3089.  
  3090.           AddAutoResource(NULL, myRoot, MY_ROOT_CLASS);
  3091.           .
  3092.           .
  3093.           .
  3094.           /*
  3095.            * No Cleanup necessary!
  3096.            * Just call RemoveCurrentProgram() and get out!
  3097.            * The MY_ROOT_CLASS wil be -terminated- automagically.
  3098.            */
  3099.           RemoveCurrentProgram(NULL);
  3100.           CloseLibrary(ShadowBase);
  3101.           .
  3102.           .
  3103.           .
  3104.  
  3105.  
  3106.      This example uses one argument specification -- a 'JSTR' -- and
  3107.      then specifies that the method returns an object.  We can call
  3108.      this method using the following (assuming this code is run
  3109.      somewhere between the CreateSubClass() and the
  3110.      RemoveCurrentProgram() calls in the above code example!):
  3111.  
  3112.           /*
  3113.            * Create the object
  3114.            */
  3115.           auto = CreateInstance(NULL, MY_ROOT_CLASS, META_CLASS,
  3116.                                                      METHOD_END);
  3117.  
  3118.           /*
  3119.            * Send off the method.
  3120.            */
  3121.           auto = DoShadow(auto, NULL, METH_CAR_REGISTER, METHOD_END);
  3122.  
  3123.                     -or-
  3124.  
  3125.           auto = DoShadow(auto, NULL, METH_CAR_REGISTER, "some object",
  3126.                                                          METHOD_END);
  3127.           /*
  3128.            * Cleanup!
  3129.            */
  3130.           RemoveObject(auto);
  3131.  
  3132.  
  3133.      This method is also safe to call asynchronously!
  3134.  
  3135.           DoShadowAsync(auto, NULL, METH_CAR_REGISTER, "some object",
  3136.                                                        METHOD_END);
  3137.  
  3138.           /*
  3139.            * Cleanup!
  3140.            *
  3141.            * It is possible for the METH_REMOVE method to be called
  3142.            * twice in this case....
  3143.            */
  3144.           RemoveObject(auto);
  3145.  
  3146.  
  3147.      Indeed, ARGUMENT_TAGs are most heavily used for the asynchronous case.
  3148.      The DoShadowAsync() attempts to invoke an asynchronous method on the
  3149.      auto.  This causes DSM() to create an IPC message with several items
  3150.      of interest -- the auto object, the string argument, and the return
  3151.      value are the only items that matter for this example.
  3152.  
  3153.      The auto object has its cob_UseCount incremented via UseObject().
  3154.      The string argument is created with the UseString() call.
  3155.      The return value is set to return an object (specifically, the
  3156.      ipc_Id is set to 'JOBJ').  The message is then sent off to the
  3157.      destination process.
  3158.  
  3159.      When the destination process receives the message, the method ends
  3160.      up being called (within ParseShadowMessage()).  The method
  3161.      specifically removes the "object" (the "auto") from the message.
  3162.      This method is defined as "swallowing" the auto-object, so the
  3163.      message is updated to reflect this.  The "auto" is then added to some
  3164.      globalTree.  If the addition is successful, the "auto" is returned.
  3165.      Otherwise the "auto" is Remove()'d from the system.
  3166.  
  3167.      When the auto is returned, it is placed into the return slot in the
  3168.      message.  ParseShadowMessage() then attempts to return the message.
  3169.      However, asynchronous messages do not have a ReplyPort, so
  3170.      ParseShadowMessage() ends up calling JunkIPCMessage() which destroys
  3171.      each item's reference and then destroys the message.
  3172.  
  3173.      In order, then, the return object is Drop()'d via DropObject(), the
  3174.      auto-object is ignored (remember the IPCARGTransfer()!), the
  3175.      string is Drop()'d via DropString(), and the message is destroyed.
  3176.  
  3177.      Note, if you hadn't specified the argument in the ARGUMENT_TAG,
  3178.      char *string would have ended up being 0x80000001 (or worse, if
  3179.      you had more arguments, those other arguments might be the return
  3180.      address of the function or stored registers, etc.).  It is very
  3181.      important that your ARGUMENT_TAG matches your method's actual
  3182.      parameter specification.  In addition, if your SHADOW_RETURN_*
  3183.      field is incorrect (or left unspecified), improper resource
  3184.      tracking of the return object might take place.  For instance, if
  3185.      the ARGUMENT_TAG were specified as follows:
  3186.  
  3187.           ARGUMENT_TAG  REF_RegisterCarMethod[] =
  3188.                            {
  3189.                               TAG_END
  3190.                            };
  3191.  
  3192.      Then the passed string would take on the value of 0x80000001 and the
  3193.      returned object in the asynchronous case would be "lost."  It would
  3194.      never be Drop()'d from the system, and thus repeated use of the
  3195.      function (asynchronous use!) would cause memory loss and
  3196.      possibly memory fragmentation.
  3197.  
  3198.      Many macros have been defined in <shadow/misc.h> which allows you
  3199.      to build ARGUMENT_TAGs more easily.  The correct
  3200.      REF_RegisterCarMethod[] would look as follows:
  3201.  
  3202.           ARGUMENT_TAG REF_RegisterCarMethod[] =
  3203.                            {
  3204.                               ARG_STR,
  3205.                               RET_OBJ
  3206.                            }
  3207.  
  3208.      Which is to say, one string argument, and the return of an object.
  3209.  
  3210.      Using these macros, we define an example which uses all of the
  3211.      important types of method arguments:
  3212.  
  3213.           ARGUMENT_TAG REF_RaceCarMethod[] =
  3214.                            {
  3215.                               ARG_OBJ,
  3216.                               ARG_STR,
  3217.                               ARG_TAGL(sizeof(struct TagItem)),
  3218.                               ARG_MESG,
  3219.                               ARG_PORT,
  3220.                               ARG_INT,
  3221.                               {'stuf', sizeof(struct StockCar), 0},
  3222.                               ARG_PTR(0),
  3223.                               RET_NORMAL
  3224.                            }
  3225.  
  3226.           /*
  3227.            * Note that the error message field is actually never
  3228.            * deleted, this should be done by the caller.  Note
  3229.            * also that deleting the message even after an
  3230.            * asynchronous method invocation works because the message
  3231.            * is -copied- for the asynchronous send, and then the copy is
  3232.            * magically deleted after the method-function is done
  3233.            * executing.
  3234.            *
  3235.            * ParseShadowMessage() is usually called with the
  3236.            * SHADOW_RETURN_MSG_NEVER, but this is an example of an
  3237.            * alternative usage pattern.  Please refer to the AutoDocs
  3238.            * for more information about ParseShadowMessage()'s flags.
  3239.            */
  3240.           BOOL RaceCarMethod(METHOD_ARGS,
  3241.                              OBJECT registry,
  3242.                              char *name,
  3243.                              struct TagItem *tags,
  3244.                              struct IPCMessage *error,
  3245.                              struct IPCPort *port,
  3246.                              long failure_probability,
  3247.                              struct StockCar sc,
  3248.                              void *garbage)
  3249.           {
  3250.                if (!...use all the arguments to race the car...)
  3251.                {
  3252.                   if (port && error)
  3253.                   {
  3254.                      error->ipc_Msg.mn_ReplyPort = GlobalReplyPort;
  3255.                      if (PutIPCMessage(port, error);
  3256.                      {
  3257.                          /*
  3258.                           * A simplified version of code to retrive
  3259.                           * the message back from the error handling port.
  3260.                           */
  3261.                          WaitPort(GlobalReplyPort);
  3262.                          GetMsg(GlobalReplyPort);
  3263.                      } else
  3264.                         ParseShadowMessage(error,
  3265.                                            SHADOW_RETURN_MSG_ALWAYS);
  3266.                   } else if (error)
  3267.                      ParseShadowMessage(error, SHADOW_RETURN_MSG_ALWAYS);
  3268.                   return FALSE;
  3269.                }
  3270.                return TRUE;
  3271.           }
  3272.  
  3273.  
  3274.      There are four specific things I want to mention with this example.
  3275.      First, the ARG_TAGL need NOT have an item size identical to the
  3276.      size of struct TagItem.  Any array of items whose last item begins
  3277.      with a zero longword is considered a valid ARG_TAGL.
  3278.  
  3279.      Second, the ARG_MESG parameter is a message which holds a "preparsed"
  3280.      version of a method call.  You can create a message to pass into
  3281.      this method by using the following as an example:
  3282.  
  3283.           errorMessage = PreParseShadow(car, NULL, METH_CAR_ERROR,
  3284.                                         "Bad Race!",
  3285.                                         METHOD_END);
  3286.  
  3287.      The errorMessage contains a message which can be sent to another port
  3288.      or Parse()d out at some later time.  The METH_CAR_ERROR method is not
  3289.      actually called; the returned message, however, contains all the
  3290.      pertinent information to call the method if you later decide to.  To
  3291.      get rid of the message, you can either call ParseShadowMessage:
  3292.  
  3293.           ParseShadowMessage(errorMessage, SHADOW_RETURN_MSG_NEVER);
  3294.  
  3295.      which will invoke the method, and then destroy the errorMessage; or
  3296.      you can call JunkIPCMessage to destroy the message:
  3297.  
  3298.           JunkIPCMessage(errorMessage);
  3299.  
  3300.      Third, the "('stuf', sizeof(struct StockCar), 0}" is an example of
  3301.      how to pass large structures on the stack to a method.  To operate
  3302.      properly on 68000 based machines, the sizeof() had better be even.
  3303.      For best performance, it should be at least long-word aligned.
  3304.  
  3305.      Lastly, the ARG_PTR(0) means to pass a pointer to the function, but
  3306.      not to copy the data to another pointer if resource tracking would
  3307.      have normally required it (ie: asynchronous methods).  If you wanted
  3308.      to pass the StockCar structure in by pointer instead of on the
  3309.      stack, you could conceivably use:
  3310.  
  3311.           ARG_PTR(sizeof(struct StockCar)),
  3312.  
  3313.      instead of:
  3314.  
  3315.           {'stuf', sizeof(struct StockCar), 0},
  3316.  
  3317.      and define the method as
  3318.  
  3319.           ., /* Args deleted! */
  3320.           .,
  3321.           .,
  3322.           struct StockCar *sc,
  3323.           void *garbage)
  3324.  
  3325.      instead of:
  3326.  
  3327.           ., /* Args deleted! */
  3328.           .,
  3329.           .,
  3330.           struct StockCar sc,
  3331.           void *garbage)
  3332.  
  3333.      This alternative should run faster than requiring stack copying as in
  3334.      the original example.
  3335.  
  3336.  
  3337.      Assumeing you had an object of the correct class, the original method
  3338.      invocation might look as follows:
  3339.  
  3340.           errorMsg = PreParseShadow(car, NULL, METH_CAR_ERROR,
  3341.                                         "Bad Race!",
  3342.                                         METHOD_END);
  3343.  
  3344.           tags[0].ti_Tag = CAR_FORMULA;
  3345.           tags[0].ti_Data = 2;
  3346.           tags[1].ti_Tag = TAG_END;
  3347.           DoShadow(car, NULL, METH_CAR_RACE, registration, "Road Runner",
  3348.                    tags, errorMsg, NULL, 85, myStockCarStructure, NULL,
  3349.                    METHOD_END);
  3350.           JunkIPCMessage(errorMsg);
  3351.  
  3352.      This would work regardless of whether the method was defined to run
  3353.      synchronously (as a function call or a synchronous message) or
  3354.      asynchronously.  This independence allows you to create code that
  3355.      will run in a wider array of circumstances, and allow you to further
  3356.      tune your application by running various parts of your program in
  3357.      separate processes.
  3358.  
  3359.  
  3360.      CreateInstance:
  3361.  
  3362.      I have been using some wrapper functions in all of my method
  3363.      discussions -- CreateInstance and CreateSubClass.  In reality, they
  3364.      are really method calls.  CreateInstance() actually contains two
  3365.      methods as in the following pseudo-code:
  3366.  
  3367.           CreateInstance(privateClass, className, metaName, <..args..>)
  3368.           {
  3369.              OBJECT obj;
  3370.  
  3371.              if (!UseObject(privateClass))
  3372.              {
  3373.                 created = TRUE;
  3374.  
  3375.                 if (metaName)
  3376.                    privateClass = FindInstanceOfMeta(className, metaName);
  3377.                 else
  3378.                    privateClass =
  3379.                     FindNodeWatchedTree(&ShadowBase->sb_metaTree,
  3380.                                         metaName);
  3381.              }
  3382.  
  3383.              /*
  3384.               * Create the object's memory.
  3385.               */
  3386.              obj = DoShadow(privateClass, NULL, METH_CREATE, METHOD_END);
  3387.              DropObject(privateClass);
  3388.  
  3389.              /*
  3390.               * Make it a real object.
  3391.               */
  3392.              ret_val= DoShadow(obj, NULL, METH_INIT, <..args..>);
  3393.              return ret_val;
  3394.           }
  3395.  
  3396.      The first method (METH_CREATE) actually creates memory for the
  3397.      object to exist in.  In addition, it sets up the class pointer for
  3398.      the object.  However, it is not, at this point, a valid object.  Were
  3399.      something to fail here, or in the METH_INIT, the only way to get the
  3400.      object to actually go away is to do the following:
  3401.  
  3402.           DropObject(UseObject(obj));
  3403.  
  3404.      This is in direct contrast to the normal "DropObject(obj)" that is
  3405.      usually called.  Programmers of METH_INIT take note!  The
  3406.      METH_DESTROY is only called when the usecount falls to zero.
  3407.      However, if it is zero to begin with (as, say, it is when it
  3408.      returns from the METH_CREATE method), you must first increment
  3409.      the usecount, and -then- decrement it.
  3410.  
  3411.      It is the METH_INIT that follows which is responsible for setting
  3412.      up the usecount to properly be non-zero.  And, of course, to setup
  3413.      the object correctly.
  3414.  
  3415.  
  3416.      The CreateSubClass is a simpler wrapper that ends up calling the
  3417.      METH_SUB method of a particular class.  The "privateClass" pointer
  3418.      lookup is exactly the same in the CreateSubClass() as in the
  3419.      CreateInstance(); however, it is the METH_SUB method that actually
  3420.      invokes the METH_CREATE and the METH_INIT, not CreateSubClass()....
  3421.  
  3422.      Wrap-up of Methods:
  3423.  
  3424.      There are many ways to call methods besides DoShadow().  We have seen
  3425.      at least two examples -- DoShadowAsync() and PreParseShadow().  The
  3426.      alternatives are documented in the ShadowLibFuncs.doc.  In
  3427.      addition, the root SHADOW library code for all of these functions is
  3428.      DSM(), and that is documented in ShadowLibraryFuncs.doc (autodoc).  I
  3429.      highly recommend using these references to learn more about the
  3430.      method invocation code.  We are about to go on to the more advanced
  3431.      topics of PATCHER_CLASS, PROCESS_CLASS and DIRECTOR_CLASS which will
  3432.      introduce even more complexity into an already complex picture, this
  3433.      is a convenient break point to try some example code and see what
  3434.      happens (let me know of any bugs!).  It will allow you to get a feel
  3435.      for the programming environment that SHADOW lives within and will
  3436.      make you feel more comfortable about the next three topics.
  3437.  
  3438.  
  3439.  
  3440.                               PATCHER CLASS
  3441.  
  3442.      You will notice that in the above discussion about methods there were
  3443.      several METH_FLAGS_* that were never covered and left later for the
  3444.      discussion about PATCHER_CLASS.  Well, the time has come.
  3445.  
  3446.      Yes, that's right, just as you thought you were beginning to
  3447.      understand everything about SHADOW's methods, some new twist pops up
  3448.      to confuse the issue.  PATCHER_CLASS is a rather good example of
  3449.      this.
  3450.  
  3451.      Essentially, all methods within all SHADOW classes are patchable.
  3452.      Patching is analogous to SetFunction(), except that great pains have
  3453.      been taken to assure that the problems with SetFunction() are NOT
  3454.      shared by PATCHER_CLASS.  Unfortunately, you also lose some of the
  3455.      flexibility of SetFunction().
  3456.  
  3457.      When a method is patched, the result is referred to as a
  3458.      "patch-chain."  DSM(), along with all of its other duties, is
  3459.      responsible for traversing the patch-chain and calling each of the
  3460.      separate method patches in turn and returning the most-recent, valid
  3461.      return value.
  3462.  
  3463.      Each patch is created in very nearly the same manner that the base
  3464.      methods were created -- through the use of the METHOD_TAG structure.
  3465.      Note, however, that each patch will require ONLY ONE METHOD_TAG
  3466.      item -- please do not attempt to pass single METHOD_TAG *items* to
  3467.      functions like SetupMethodTags() which requires a METHOD_TAG *array*.
  3468.      This will not work, so you'll have to setup the mtag_procObject and
  3469.      mtag_defnObject fields by hand (or put all of your METHOD_TAG patches
  3470.      in one big array and call SetupMethodTags() on this array.
  3471.  
  3472.      There are two significant differences between the METHOD_TAGs that
  3473.      are used for the base methods and the METHOD_TAGs that are used for
  3474.      patches.  The former ignores the mtag_priority field while the
  3475.      latter uses it to order the patch-chain.  The patches in the patch
  3476.      chain are called in descending numerical order where the base
  3477.      method is always assumed to exist at priority zero.
  3478.  
  3479.      The second difference is the additional mtag_flags values which,
  3480.      while supported by the base methods, gain real power only when
  3481.      patch-chains exist:
  3482.  
  3483.           METH_FLAG_PRE_BLOCK
  3484.           METH_FLAG_POST_BLOCK
  3485.           METH_FLAG_CHECK_CONTINUE
  3486.           METH_FLAG_NO_RTRN
  3487.  
  3488.  
  3489.      METH_FLAG_PRE_BLOCK:
  3490.      If this flag is set, DSM() will stop invoking the patches within the
  3491.      patch-chain.  Additionally, the patch/base-method itself will not
  3492.      get invoked.  This is useful if you want to interrupt your
  3493.      patch-chain during the debugging phase, or if you want to disable
  3494.      some of the features in a demo version -- simply add the PRE_BLOCK to
  3495.      the base methods' mtag_flags, or add a PRE_BLOCK'd patch at some
  3496.      relatively high priority to the class to be affected.
  3497.  
  3498.      METH_FLAG_POST_BLOCK:
  3499.      If this flag is set, DSM() will stop invoking the patches within the
  3500.      patch-chain, but only after invoking this patch/base-method.  This is
  3501.      useful for replacing a base-method during a bug-release update.  Add
  3502.      a POST_BLOCK'd patch to the base-method at a priority greater than
  3503.      zero and the base-method is effectively replaced by the patch.
  3504.  
  3505.      METH_FLAG_CHECK_CONTINUE:
  3506.      If this flag is set, the patch returns an additional value in d1 which
  3507.      informs DSM() whether or not to continue the patch.  This allows you
  3508.      to dynamically change whether your patch executes, or the base-method
  3509.      executes.  If d1 is returned as zero from the patch, then the
  3510.      patch-chain invocation ends and the latest, valid return value is
  3511.      returned.  Otherwise, the patch-chain continues as it would have.
  3512.  
  3513.      METH_FLAG_NO_RTRN:
  3514.      This informs DSM() that your patch or method does not return a valid
  3515.      value, so any previous return values that have accumulated while
  3516.      traversing the patch-chain should remain intact.  Note that this is
  3517.      a significant inversion of meaning from SHADOW 4's
  3518.      METHOD_FLAG_RTRN_ME.
  3519.  
  3520.  
  3521.      Several important notes should be made at this point.  First, the
  3522.      traversal through the patch chain is controlled by a semaphore on the
  3523.      patch-chain's list.  This means that if you attempt to do something
  3524.      silly like have a base-method which patches itself, or a patch/base-
  3525.      method which attempts to remove a of the patch, the semaphore will
  3526.      nicely lock up your code.  Secondly, the traversal through the
  3527.      patch chain is guaranteed to be serial.  This means that even
  3528.      if you call DoShadowAsync(), the method will actually be preparsed,
  3529.      sent to the base-method's mtag_procObject asynchronously, and only
  3530.      then will the patch-chain invocation will occur.  This allows
  3531.      behaviour which depends on serial traversal (like the
  3532.      FLAG_CHECK_CONTINUE) to continue to operate correctly, even though
  3533.      each patch is actually invoked asynchronously to the invoker.
  3534.  
  3535.      Third, while patches are invoked from high priority to low priority,
  3536.      the actual return value will be the return value from the -lowest-
  3537.      priority patch that ended up returning a valid return value.  Fourth,
  3538.      the method that is being patched must ALREADY exist within the class
  3539.      that's being patched -- the only way to add new methods is to modify
  3540.      the superclass hierarchy -- this will be covered at the end of the
  3541.      PATCHER_CLASS description.  Fifth, there is a speed penalty to using
  3542.      patch-chains -- do not expect them to ever be very speedy.  And last,
  3543.      but certainly not least, because DSM() controls the patch-chain
  3544.      traversal, there is no way to modify the arguments as they travel
  3545.      from patch to patch.  Please be careful and act accordingly.
  3546.  
  3547.  
  3548.      The following code demonstrates the PATCHER_CLASS in action:
  3549.  
  3550.           /*
  3551.            * The patches
  3552.            */
  3553.           extern double PreTestMethod(METHOD_ARGS);
  3554.           extern long PostTestMethod(METHOD_ARGS);
  3555.  
  3556.           METHOD_TAG preMethod =
  3557.                                      {
  3558.                                         "Method TEST",
  3559.                                         NULL, NULL,
  3560.                                         INVOKE_SYNC,
  3561.                                         METH_FLAG_CHECK_CONTINUE |
  3562.                                         METH_FLAG_CLASS |
  3563.                                         METH_FLAG_NO_RTRN,
  3564.                                         1, /* The priority */
  3565.                                         (METHODFUNCTYPE)PreTestMethod,
  3566.                                            NULL
  3567.                                      };
  3568.  
  3569.           METHOD_TAG postMethod =
  3570.                                      {
  3571.                                         "Method TEST",
  3572.                                         NULL, NULL,
  3573.                                         INVOKE_SYNC,
  3574.                                         METH_FLAG_OBJECT |
  3575.                                         METH_FLAG_NO_RTRN,
  3576.                                         -1,
  3577.                                         (METHODFUNCTYPE)PostTestMethod,
  3578.                                            NULL
  3579.                                      };
  3580.  
  3581.           /*
  3582.            * Add pre and post patches to the dosClass.
  3583.            * Note that the "Method TEST" method MUST ALREADY exist
  3584.            *  in the dosClass definition.
  3585.            */
  3586.           preMethod.mtag_procObject = dosTaskClass;
  3587.           preMethod.mtag_defnObject = CurrentProcess();
  3588.  
  3589.           preObject = CreateInstance(NULL, PATCHER_CLASS, META_CLASS,
  3590.                                                           &preMethod,
  3591.                                                           dosClass,
  3592.                                                           METHOD_END
  3593.  
  3594.           postMethod.mtag_procObject = dosTask;
  3595.           postMethod.mtag_defnObject = CurrentProcess();
  3596.  
  3597.           postObject = CreateInstance(NULL, PATCHER_CLASS, META_CLASS,
  3598.                                                            &postMethod,
  3599.                                                            dosClass,
  3600.                                                            METHOD_END);
  3601.  
  3602.  
  3603.           /*
  3604.            * The method has now been patched!
  3605.            * Now, when you call the method, the preMethod is invoked in an
  3606.            * instantiated dosTaskClass-process; then the regular method is
  3607.            * invoked; then the postMethod is invoked synchronously in the
  3608.            * dosTask process.
  3609.            */
  3610.           .
  3611.           .
  3612.           .
  3613.           /*
  3614.            * Remove the patches.
  3615.            * You could have added this to the process with the
  3616.            *  AddAutoResource() call, in which case you would not
  3617.            *  need to call RemoveObject()....
  3618.            */
  3619.           RemoveObject(preObject);
  3620.           RemoveObject(postObject);
  3621.  
  3622.  
  3623.  
  3624.          /*
  3625.           * The patcher methods.
  3626.           *
  3627.           * The PreTestMethod() uses a SAS/C 5.0 side-effect where
  3628.           *  a double is returned in d0 and d1.
  3629.           * Whether the patch-chain continues or not alternates
  3630.           *  between TRUE and FALSE.
  3631.           */
  3632.           double PreTestMethod(METHOD_ARGS)
  3633.           {
  3634.              static int __far myLocalVar = 0; /* YES -- __far is
  3635.                                                         required! */
  3636.              union {
  3637.                 double tempD;
  3638.                 ULONG  tempV[2];
  3639.              } retval;
  3640.              BPTR oldoutput;
  3641.  
  3642.              oldoutput = SelectOutput(Open("CONSOLE:", MODE_OLDFILE));
  3643.  
  3644.              VPrintf("Pretest called in <%s> task.\n",
  3645.                      (ULONG *)&(FindTask(NULL)->tc_Node.ln_Name));
  3646.  
  3647.              if (!(retval.tempV[1] = (myLocalVar++ & 1)))
  3648.                 VPrintf("PreTest will block this call:\n", NULL);
  3649.  
  3650.              oldoutput = SelectOutput(oldoutput);
  3651.              Close(oldoutput);
  3652.              retval.tempV[0] = 0;  /* This number is NOT a valid return!
  3653.                                     * Note the METH_FLAG_NO_RTRN
  3654.                                     */
  3655.              return retval.tempD;
  3656.           }
  3657.  
  3658.           /*
  3659.            * Note that the "return 200" is NOT a valid return as the
  3660.            * METHOD_TAG has the METH_FLAG_NO_RTRN flag set.
  3661.            *
  3662.            * As the preMethod and the postMethod never return a
  3663.            *  valid return value, the return value of the base-method
  3664.            *  is returned, -if- the preMethod doesn't return a FALSE (which
  3665.            *  would end the patch-chain, and therefore prevent the base-
  3666.            *  method from being called in the first place.  In that case,
  3667.            *  the DSM() [DoShadow(), et. al.] returns zero.
  3668.            */
  3669.           long PostTestMethod(METHOD_ARGS)
  3670.           {
  3671.              VPrintf("Post test called\n", NULL);
  3672.              return 200;
  3673.           }
  3674.  
  3675.  
  3676.      While PATCHER_CLASS allows you to patch existing methods, it does not
  3677.      allow you to add additional methods.  There is a way to add methods
  3678.      to a class, unfortunately, there is no way to later -remove- those
  3679.      methods.  The way in which this occurs is to change the superclass
  3680.      hierarchy so that the class that you want the method to be added to
  3681.      is changed to be a subclass of a class that is created with the
  3682.      method you desire.
  3683.  
  3684.      In otherwords, if you wish to add a method to the PROCESS_CLASS,
  3685.      you create an intermediate class with the additional method, then
  3686.      point the superclass of the PROCESS_CLASS to the intermediate class.
  3687.      It is, of course, required that the intermediate class be a subclass
  3688.      of the superclass of the "PROCESS_CLASS", or, the class your adding
  3689.      the method to.  You can use the SHADOW library function call --
  3690.      InsertSuperClass(), or the METH_SUPER method call as demonstrated
  3691.      below:
  3692.  
  3693.           /*
  3694.            * Creates a new superclass of a subclass.
  3695.            * Adds the methods specific in the my_new_method structure.
  3696.            */
  3697.           procClass = FindShadowClass(PROCESS_CLASS, META_CLASS);
  3698.           newClass = DoShadow(procClass, NULL, METH_SUPER,
  3699.                               MY_INTERMEDIATE_CLASS,
  3700.                                           NULL,
  3701.                                           my_new_attributes,
  3702.                                           my_new_methods,
  3703.                                           METHOD_END);
  3704.           DropObject(procClass);
  3705.  
  3706.           .
  3707.           .
  3708.           .
  3709.  
  3710.           RemoveObject(newClass);
  3711.  
  3712.  
  3713.      This example is taken directly from the ShadowLibraryMethods.doc,
  3714.      where you can find more information about the METH_SUPER method.  In
  3715.      addition, you can find out more about adding new methods by reading
  3716.      the InsertSuperClass() method.
  3717.  
  3718.      Obviously, the preferred manner of adding methods to classes is to
  3719.      -subclass- them and create your own classes.  However, PATCHER_CLASS
  3720.      exists to provide as much flexibility and extensibility as possible.
  3721.  
  3722.  
  3723.                              DIRECTOR CLASS
  3724.  
  3725.      DIRECTOR_CLASS expands upon the notion of extensibility by providing
  3726.      notification for important system and program state changes.  For
  3727.      instance, you can receive notification whenever a class is added
  3728.      or removed from the system list, or when a -particular- class is
  3729.      added or removed from the system.  In the gui I've been
  3730.      experimenting with, the ATTR_GUICHILDREN attribute which is a
  3731.      "watched" binary tree that contains a GUI-object's children,
  3732.      generates notification whenever any children are removed or
  3733.      added to the system.
  3734.  
  3735.      In addition to being able to watch a particular GUI-object, you can
  3736.      watch all the GUI-object's of any particular class (and associated
  3737.      subclasses) that you'd like.  It is entirely feasible to imagine
  3738.      receiving notification whenver any program added any GUI-object to
  3739.      the child tree of any other GUI-object.
  3740.  
  3741.      DIRECTOR_CLASS objects handle the request for notification of these
  3742.      system and program state changes.  These state changes occur in
  3743.      special kinds of "state" or "watched" variables.  You can create a
  3744.      WatchedVariable in the following manner:
  3745.  
  3746.      void *nullPtr = NULL;
  3747.      struct WatchedVariable   wv = {NULL, &nullPtr, initial_value};
  3748.  
  3749.      The &wv can then be used when establishing a connection in
  3750.      the DIRECTOR_CLASS.  However, as SHADOW is mainly an object-
  3751.      oriented system (or, rather, it seems to have grown into being one),
  3752.      it is more frequent to find yourself establishing a director-object
  3753.      connection to a watched variable inside of a class.
  3754.  
  3755.      A watched variable inside of a class is created in a very special
  3756.      way that allows you not only to watch a single object, but all the
  3757.      objects that are instances of a particular class (and instances of
  3758.      all classes that are subclasses of that particular class).
  3759.  
  3760.      Here is an example attribute definition:
  3761.  
  3762.           ATTRIBUTE_TAG guiAttrs[] =
  3763.                      {
  3764.                         {
  3765.                            ATTR_GUICHILDREN, FLAG_ATTR_WATCHED |
  3766.                                              SHADOW_TREE,
  3767.                            NULL
  3768.                         },
  3769.                         TAG_END
  3770.                      }
  3771.  
  3772.      This attribute creates a watched variable that allows you to use
  3773.      the attribute as an AVL tree.  Although, instead of calling the
  3774.      regular AddTreeNode() and/or RemoveTreeStringNode(), you call the
  3775.      AddWatchedTreeNode() and/or RemoveWatchedTreeStringNode().  As a
  3776.      director watching this attribute, you can get notification about
  3777.      when a node is added or removed from the tree (you can also
  3778.      receive notification when some other director establishes or
  3779.      terminates a connection to any watched variable, but that gets
  3780.      just a little ridiculous...).
  3781.  
  3782.      There are three basic types of watched variables.  One is the
  3783.      long-word type, another is the AVL tree type which is shown
  3784.      above, and the last is the singly-linked list type which I
  3785.      won't bother to cover in the examples.  These three types
  3786.      are denoted by three different flags located in <shadow/watcher.h>:
  3787.           SHADOW_VALUE
  3788.           SHADOW_TREE
  3789.           SHADOW_LIST
  3790.      Use these flags in the ATTRIBUTE_TAG value when creating a
  3791.      watched variable in an object.
  3792.  
  3793.      Watching a watched variable is a two-step process.  First you
  3794.      need to create an instance of a director class.  There are several
  3795.      things to setup at this phase -- the name of the director
  3796.      object, the object that will receive the notification and the
  3797.      selector that will be invoked on that object, the type of events
  3798.      you want to receive notification about, the type of watcher, and
  3799.      the default resource tracking cleanup algorithms that will be
  3800.      used when the director's connections are terminated and the
  3801.      director is removed from the system.
  3802.  
  3803.      Second, you need to extablish a connection with a particular watched
  3804.      variable/attribute or class of watched attributes.  Unlike method
  3805.      patches which are can only be installed in a single class at a time,
  3806.      director objects can establish themselves in any number of watched
  3807.      variables -- allowing you to save, perhaps, a bit of memory in the
  3808.      process....  The ESTABLISH method only takes arguments which
  3809.      defines the watched variable and returns a handle to the connection
  3810.      which should either be sent to DropObject(), or used in any
  3811.      explicit call to the METH_DIRECTOR_TERMINATE method.
  3812.  
  3813.      Setting up a director object is less complex than method patches, but
  3814.      nevertheless there are a few flags you should know about [the
  3815.      following flags are located in <shadow/watcher.h>]:
  3816.  
  3817.           /*
  3818.            * Match either.
  3819.            */
  3820.           W_CHANGE_ZERO
  3821.           W_CHANGE_NON_ZERO
  3822.           W_CHANGE_VALUE
  3823.  
  3824.           /*
  3825.            * Match exactly
  3826.            */
  3827.           W_NODE
  3828.           W_WATCH_CHANGE
  3829.           W_REMOVE
  3830.           W_INSERT
  3831.  
  3832.           /*
  3833.            * Control matching.
  3834.            */
  3835.           W_MATCH
  3836.           W_MATCH_VALUE      W_MATCH
  3837.           W_SECOND
  3838.  
  3839.           W_MATCH_FIRST      W_MATCH
  3840.           W_MATCH_SECOND     (W_SECOND | W_MATCH)
  3841.  
  3842.           W_INSERT_NODE      (W_NODE | W_INSERT)
  3843.           W_REMOVE_NODE      (W_NODE | W_REMOVE)
  3844.  
  3845.           W_INSERT_WATCHER   (W_WATCH_CHANGE | W_INSERT)
  3846.             /* Not valid during... */
  3847.           W_REMOVE_WATCHER   (W_WATCH_CHANGE | W_REMOVE)
  3848.             /* ... INIT[]/DESTROY[]  */
  3849.  
  3850.      The exact meaning of all of those flags is covered in detail under
  3851.      the METH_DIRECTOR_NOTIFY method description in ShadowMethods.doc.
  3852.      In general, these flags describe which state-change events the
  3853.      director object is interested in getting notification about.
  3854.  
  3855.           /*
  3856.            * for use in passing to METH_INIT....
  3857.            */
  3858.           W_FLAG_AUTOBREAK
  3859.           W_FLAG_AUTOREMOVE
  3860.  
  3861.  
  3862.      Do -not- confuse these with the W_AUTOBREAK and W_AUTOREMOVE flags
  3863.      which are NOT to be passed into the METH_INIT function!
  3864.      The W_FLAG_AUTOBREAK informs the director object that when the
  3865.      director is sent a METH_REMOVE method, it should terminate all of
  3866.      its outstanding connections.
  3867.      The W_FLAG_AUTOREMOVE informs the director object that when the
  3868.      last connection is terminated, the director should automatically
  3869.      be removed from the system.
  3870.      By default, I suggest you use both flags unless you think of some
  3871.      -very- good reason not to.
  3872.  
  3873.           W_OBJECT
  3874.           W_CLASS
  3875.  
  3876.      These two flags specify which type of director you wish to create --
  3877.      one to watch an entire class, or one to watch just a specific
  3878.      object or watched variable.
  3879.  
  3880.  
  3881.           /*
  3882.            * As flag to TERMINATE METHOD.
  3883.            */
  3884.           W_HANDLE
  3885.           W_SORT
  3886.  
  3887.      There are three ways to TERMINATE a connection.  The first is to
  3888.      create the director with the W_FLAG_AUTOBREAK flag set and then
  3889.      send a METH_REMOVE method (ie. RemoveObject()) to the director.
  3890.      This will terminate all connections.  The second is to specify,
  3891.      using the handle returned to you by the ESTABLISH call, a specific
  3892.      connection be TERMINATEd.  This is the W_HANDLE case, and if left
  3893.      unspecified, the default manner is W_HANDLE.  The third case is to
  3894.      specify an object that the director was watching and TERMINATE any
  3895.      connection in that object -- however, you are not guaranteed
  3896.      -which- connection is terminated, if you are watching more than one
  3897.      attribute, or one attribute more than once, or some combination
  3898.      thereof.  This third possibility is usually used by the system,
  3899.      rather than the external programmer -- there are very arcane
  3900.      reasons for why this needs to exist....
  3901.  
  3902.  
  3903.      There are two different types of directors -- class-watchers and
  3904.      object-watchers.  Herein we demonstrate both:
  3905.  
  3906.           /*
  3907.            * Create a director object.
  3908.            *    Specify the director's name as "WatchWindows".
  3909.            *    Specify the notification be invoked on "object" with the
  3910.            *       METH_TELL_ME method.
  3911.            *    Specify interest in addition or removal of nodes from a
  3912.            *       complex watched variable like SHADOW_TREE or SHADOW_LIST
  3913.            *    Specify a class watcher
  3914.            *    Specify the autoremove and autobreak features.
  3915.            */
  3916.           GlobalDirector = CreateInstance(NULL,
  3917.                                           DIRECTOR_CLASS,
  3918.                                           META_CLASS,
  3919.                                           "WatchWindows",
  3920.                                           object,
  3921.                                           METH_TELL_ME,
  3922.                                           W_INSERT_NODE | W_REMOVE |
  3923.                                            W_CLASS |
  3924.                                            W_FLAG_AUTOBREAK  |
  3925.                                            W_FLAG_AUTOREMOVE,
  3926.                                           METHOD_END);
  3927.  
  3928.           /*
  3929.            * Let's watch for additions and removal of children to any
  3930.            * object of the WINDOW_CLASS, or any instance of any
  3931.            * subclass of the WINDOW_CLASS....  The watched-tree
  3932.            * attribute that contains the windows' children is
  3933.            * called ATTR_GUICHILDREN.  It is not specific to just the
  3934.            * WINDOW_CLASS, though....
  3935.            *
  3936.            * Note that we throw away the handle that the ESTABLISH
  3937.            * method returns.
  3938.            */
  3939.           windowClass = FindShadowClass(WINDOW_CLASS);
  3940.           DropObject(DoShadow(GlobalDirector,
  3941.                                   NULL,
  3942.                                   METH_DIRECTOR_ESTABLISH,
  3943.                                   ATTR_GUICHILDREN,
  3944.                                   windowClass,
  3945.                                   METHOD_END));
  3946.           DropObject(windowClass);
  3947.  
  3948.  
  3949.           .
  3950.           .
  3951.           .
  3952.  
  3953.           /*
  3954.            * Cleanup!
  3955.            * We didnt't add the GlobalDirector to our AutoResource() tree,
  3956.            * so we'll have to do an explicit RemoveObject()!
  3957.            * Remember, the W_FLAG_AUTOBREAK means that the METH_REMOVE
  3958.            * terminates all connections.
  3959.            */
  3960.  
  3961.           RemoveObject(GlobalDirector);
  3962.  
  3963.  
  3964.      The following example will demonstrate a director watching an object,
  3965.      or watching a plain watched variable that does not exist within an
  3966.      object:
  3967.  
  3968.           /*
  3969.            * Create a director object.
  3970.            *    Specify the director's name as "Watch Meta".
  3971.            *    Specify the notification be invoked on "object" with the
  3972.            *       METH_INFORM_ME method.
  3973.            *    Specify interest in addition or removal of nodes from a
  3974.            *       complex watched variable like SHADOW_TREE or SHADOW_LIST
  3975.            *    Specify an object/variable watcher
  3976.            *    Specify the autoremove and autobreak features.
  3977.            */
  3978.           mlo->director = CreateInstance(NULL,
  3979.                                          DIRECTOR_CLASS,
  3980.                                          META_CLASS,
  3981.                                          "Watch Meta",
  3982.                                          object,
  3983.                                          METH_INFORM_ME,
  3984.                                          W_INSERT_NODE | W_REMOVE |
  3985.                                           W_OBJECT << 16 |
  3986.                                           W_FLAG_AUTOBREAK |
  3987.                                           W_FLAG_AUTOREMOVE,
  3988.                                          METHOD_END);
  3989.  
  3990.           /*
  3991.            * If there is a specific 'meta' to watch, let's watch
  3992.            * for additions or removals of instances to/from the meta's
  3993.            * ATTR_INSTANCETREE.  If there is no meta (that is, if meta
  3994.            * is NULL), let's watch SHADOW's public Watched Variable
  3995.            * which contains all the current metas and watch for a
  3996.            * meta to be added or removed.
  3997.            *
  3998.            * Note that we throw away the handle that the ESTABLISH
  3999.            * method returns.
  4000.            *
  4001.            * Note, we pass in the object and the object's attribute
  4002.            * that we want notification about (meta dn ATTR_INSTANCETREE
  4003.            * in this case.  If the object is NULL, the attribute, which
  4004.            * is usually a string, is now assume to point to a watched
  4005.            * variable structure instead.
  4006.            *
  4007.            * Yes, we really want a W_OBJECT watcher to watch a meta.
  4008.            * Why?  Because we are watching -one- meta, not a whole
  4009.            * *class* of metas, which is where we would want to use a
  4010.            * W_CLASS type of director.  In otherwords, if you wanted to
  4011.            * watch the ATTR_INSTANCETREE of a  whole class of metas,
  4012.            * you'd use a W_CLASS.  Here, though, we need a W_OBJECT
  4013.            * director object.
  4014.            */
  4015.  
  4016.           DropObject( DoShadow(mlo->director,
  4017.                                NULL,
  4018.                                METH_DIRECTOR_ESTABLISH,
  4019.                                (meta)?ATTR_INSTANCETREE:
  4020.                                      &ShadowBase->sb_metaTree,
  4021.                                meta,
  4022.                                METHOD_END));
  4023.  
  4024.  
  4025.           .
  4026.           .
  4027.           .
  4028.  
  4029.           /*
  4030.            * Cleanup!
  4031.            * We didnt't add the mlo->director to our AutoResource() tree,
  4032.            * so we'll have to do an explicit RemoveObject()!
  4033.            * Remember, the W_FLAG_AUTOBREAK means that the METH_REMOVE
  4034.            * terminates all connections.
  4035.            */
  4036.  
  4037.           RemoveObject(mlo->director);
  4038.  
  4039.  
  4040.      For this example, we explore explicit TERMINATion of a director's
  4041.      connections.
  4042.  
  4043.           /*
  4044.            * Create a director object.
  4045.            *    Specify the director's name as "WatchBlockedChildren".
  4046.            *    Specify the notification be invoked on "object" with the
  4047.            *       METH_SLAP_ME_ONCE method.
  4048.            *    Specify interest in addition or removal of nodes from a
  4049.            *       complex watched variable like SHADOW_TREE or SHADOW_LIST
  4050.            *    Specify an object/variable watcher
  4051.            *    Specify the autoremove and autobreak features.
  4052.            */
  4053.           director = CreateInstance(NULL,
  4054.                                     DIRECTOR_CLASS,
  4055.                                     META_CLASS,
  4056.                                     "WatchBlockedChildren",
  4057.                                     object,
  4058.                                     METH_SLAP_ME_ONCE
  4059.                                     W_INSERT_NODE | W_REMOVE |
  4060.                                      W_OBJECT << 16 |
  4061.                                      W_FLAG_AUTOBREAK |
  4062.                                      W_FLAG_AUTOREMOVE,
  4063.                                     METHOD_END);
  4064.  
  4065.           /*
  4066.            * Establish the connection for the director.
  4067.            * Here, we'll watch the ATTR_GUICHILDREN of an object.
  4068.            */
  4069.           handle1 = DoShadow(director, NULL, METH_DIRECTOR_ESTABLISH,
  4070.                                              ATTR_GUICHILDREN,
  4071.                                              object,
  4072.                                              METHOD_END);
  4073.  
  4074.           /*
  4075.            * Establish another connection.
  4076.            * Here, we'll watch the ATTR_INSTANCETREE of WINDOW_CLASS.
  4077.            * Note that we aren't interested in the INSTANCE_TREE
  4078.            * attribute for -all- classes, just for the on particular
  4079.            * -class-instance- -- WINDOW_CLASS, which is why we want
  4080.            * a W_OBJECT watcher as opposed to a W_CLASS watcher.
  4081.            */
  4082.           windows = FindShadowClass(WINDOW_CLASS);
  4083.           handle2 = DoShadow(director, NULL, METH_DIRECTOR_ESTABLISH,
  4084.                                              ATTR_INSTANCETREE,
  4085.                                              windows,
  4086.                                              METHOD_END);
  4087.           DropObject(windows);
  4088.  
  4089.           .
  4090.           .
  4091.           .
  4092.  
  4093.           /*
  4094.            * Terminate the first connection.
  4095.            */
  4096.           DoShadow(director, NULL, METH_DIRECTOR_TERMINATE, handle1,
  4097.                                                             METHOD_END);
  4098.  
  4099.           .
  4100.           .
  4101.           .
  4102.           /*
  4103.            * Terminate the second connection.
  4104.            */
  4105.           DoShadow(director, NULL, METH_DIRECTOR_TERMINATE, handle2,
  4106.                                                             METHOD_END);
  4107.  
  4108.           .
  4109.           .
  4110.           .
  4111.           /*
  4112.            * Cleanup!
  4113.            *
  4114.            * Note, because we specified the W_AUTOREMOVE feature, and
  4115.            * because we've closed all of the connections, the director
  4116.            * has already been removed.  Therefore, simply DropObject()
  4117.            * it.
  4118.            */
  4119.           DropObject(director);
  4120.  
  4121.  
  4122.      This code is taken directly from the Browser included in the SHADOW
  4123.      distribution.  Browser uses W_OBJECT watchers to maintain a current
  4124.      list of the available classes, objects, and number of patches on a
  4125.      method.  If you add a class while the Browser is showing a list of
  4126.      the classes, that list is updated automatically, without the active
  4127.      knowledge of the program creating the class!
  4128.  
  4129.  
  4130.  
  4131.                               PROCESS CLASS
  4132.  
  4133.      Process classes offer several hurdles that are not encountered
  4134.      with any other objects.  First, the initialization of a process is
  4135.      primarily a two-stage procedure -- the creation of an AmigaDOS
  4136.      process, and then the initialization of all of the ports, libraries,
  4137.      etc. that that particular task requires.  Note that the second
  4138.      stage -must- occur within the created task, whereas the first
  4139.      stage -cannot- occur within that task (it wouldn't be around at
  4140.      that point).  Second, the cleanup of processes (ie: the removal
  4141.      of ports and libraries bases, etc.) must occur within the task
  4142.      itself, but cannot actually occur until all references to the
  4143.      process object are eliminated.  On the otherhand, many of the
  4144.      resources added to a process via the AddAutoResource() call
  4145.      reference the process object (usually indirectly, through
  4146.      an mtag_defnObject field in a method within a class, for instance).
  4147.      Therefore, the METH_REMOVE must clear all of the automatic
  4148.      resources, but the METH_DESTROY is responsible for actually
  4149.      closing down the ports, allocated signals, and library
  4150.      bases.
  4151.  
  4152.      Additionally, process creation must take into account that a program
  4153.      launched via the CLI or the Workbench does not have a SHADOW
  4154.      process object associated with it, and therefore does not contain
  4155.      the requisite ports and messages to deal with the sending and
  4156.      receiving of SHADOW methods.  There must be a way to associate
  4157.      an already existing process with a SHADOW process object after
  4158.      the process creation (note how similar this requirement is to
  4159.      the two-stage startup procedure described in the above paragraph).
  4160.  
  4161.      Finaly, processes should provide an example on how to properly deal
  4162.      with SHADOW messages by providing a default message handler which
  4163.      handles all of the various SHADOW methods and returns when the
  4164.      process gets a ^C (a ^C is automatically sent to the process by
  4165.      the system under certain shut-down conditions as well).
  4166.  
  4167.  
  4168.      Process and Program Startup and initialization
  4169.  
  4170.      The initialization of a process object is separated into two separate
  4171.      parts -- a METH_INIT and a METH_PROC_ASSOCIATE.  The METH_INIT
  4172.      takes several parameters:
  4173.           The name of the process
  4174.           One of two parameters that prevents a parent process from
  4175.              exitting before its children
  4176.           A taglist that is passed to the CreateNewPoc() AmigaDOS call
  4177.           A ProcessInitFrame    -- for the METH_PROC_ASSOCIATE call
  4178.           A ProcessHandlerFrame -- for the METH_PROC_HANDLER call
  4179.  
  4180.      Let's take them one at a time, ignoring the process name argument,
  4181.      as its function is obvious.
  4182.  
  4183.      Let us say that you start a thread on one of your processes.  You
  4184.      definitely do not want your parent process going away before your
  4185.      child threads have all disappeared -- there are two ways to prevent
  4186.      this.  First, you can pass an Exec semaphore pointer to the
  4187.      INIT code.  The thread that is created will grab a SHARED lock on
  4188.      this semaphore between the time that the METH_INIT is called,
  4189.      and the time when the METH_INIT returns (the magic involved therein
  4190.      is unimportant).
  4191.  
  4192.      Therefore, before your parent process disappears, it should attempt
  4193.      to grab an exclusive lock on this semaphore.  You can accomplish
  4194.      this by passing the semaphore into the RemoveCurrentProgram()
  4195.      call that all SHADOW programs call in their cleanup code.
  4196.  
  4197.      A much easier way (and the preferred SHADOW V way to deal with these
  4198.      issues) is to pass the parent's own process object into the the
  4199.      METH_INIT call.  SHADOW will then resource track the parent process
  4200.      within the child thread.  The parent process will be dropped only
  4201.      after the program returns from the _HANDLER routine, thereby
  4202.      preventing the parent process from disappearing before its children.
  4203.  
  4204.      The taglist is a taglist that is passed to the CreateNewProc()
  4205.      library call under AmigaDOS.  Several tags are reserved for
  4206.      exclusive use by SHADOW.  They are:
  4207.           NP_Name
  4208.           NP_ExitData
  4209.           NP_Entry
  4210.  
  4211.      NP_Name is set to the name passed into the INIT call.
  4212.      NP_ExitData is used internally.
  4213.      NP_Entry is used internally.  You should use the ProcessHandlerFrame
  4214.      to set your entrypoint -- this will be a -method- entry-point....
  4215.  
  4216.      The ProcessInitFrame is a METHOD_END-terminated array of the
  4217.      arguments that will be used to finish the creation of the process.
  4218.      If NULL, the internal definition of the arguments that
  4219.      METH_PROC_ASSOCIATE takes is used.  You may override which method is
  4220.      called to complete the process initialization by specifying a non-
  4221.      NULL field for the pif_methodID field.  If NULL, the method is
  4222.      assumed to be METH_PROC_ASSOCIATE.  In addition, you may add any
  4223.      number of arguments to the end of the InitFrame, though the
  4224.      final arguments should all be taken by the destination method.
  4225.  
  4226.      The following is the internal ProcessInitFrame that is used by
  4227.      SHADOW.  It resides in <shadow/process.h>:
  4228.  
  4229.           struct ProcessInitFrame {
  4230.              struct CoreObject *pif_object;
  4231.              struct Meta       *pif_class;
  4232.              char              *pif_methodID;
  4233.              char              *pif_procName;
  4234.              void              *pif_args[1];  /*
  4235.                                                * Other arguments,
  4236.                                                * terminated by METHOD_END
  4237.                                                */
  4238.           };
  4239.  
  4240.  
  4241.      The METH_INIT code fills in the object, and the class, and changes the
  4242.      methodID as required.  If the ProcessInitFrame is passed in, the
  4243.      pif_procName is not touched, and the rest of the arguments are
  4244.      assumed to be valid and terminated by a METHOD_END.  The entire
  4245.      array is passed to DSM() which returns a message encapsulating
  4246.      the appropriate method call.  The actual invocation of this
  4247.      method will occur once the process starts up.
  4248.  
  4249.      The ProcessHandlerFrame is the entry point (method) into the thread.
  4250.      By default, this is the METH_PROC_HANDLER method which takes no
  4251.      arguments.  As with the ProcessInitFrame structure, the actual
  4252.      method can be overrided by passing in your own methodID in the
  4253.      ProcessHandlerFrame.  A NULL methodID field in a passed
  4254.      ProcessHandlerFrame is assumed to mean METH_PROC_HANDLER.
  4255.  
  4256.      The following is the internal ProcessHandlerFrame that is used by
  4257.      SHADOW.  It also resides in <shadow/process.h>:
  4258.  
  4259.           struct ProcessHandlerFrame {
  4260.              struct CoreObject *phf_object;
  4261.              struct Meta       *phf_class;
  4262.              char              *phf_methodID;
  4263.              void              *phf_args[1];  /*
  4264.                                                * Other arguments,
  4265.                                                * terminated by METHOD_END
  4266.                                                */
  4267.           };
  4268.  
  4269.      Once again, either the default ProcessHandlerFrame, or one that you
  4270.      provide, is passed into DSM() and a message is returned.  During
  4271.      the internal startup code of the thread, the ProcessHandlerFrame's
  4272.      message is invoked subsequent to the ProcessInitFrame's message.
  4273.  
  4274.      Once both of these messages are created, the actual AmigaDOS process
  4275.      is created.  The METH_INIT will not return yet, though, the
  4276.      METH_PROC_ASSOCIATE (or the method specified in the ProcessInitFrame)
  4277.      is allowed to run first.  If this method returns 0L, then the
  4278.      initialization is assumed to have failed, the thread shuts down, and
  4279.      the METH_INIT returns a NULL process object.  If this method
  4280.      returns a non-zero value, the parent process is signalled to
  4281.      wakeup, and the thread calls the METH_PROC_HANDLER method (or
  4282.      the method specified in the ProcessHandlerFrame).
  4283.  
  4284.      When the parent process gets woken up, it attempts to discover
  4285.      whether or not the process ended up working or not, and if it did,
  4286.      the process object is returned, otherwise NULL is returned.  In
  4287.      addition, if your process is created, the tag entries are
  4288.      TAG_IGNORE'd -- this way you know whether you need to free any
  4289.      resources the tags might have pointed to (like file pointers for
  4290.      stdin and stdout).  Note that these fields are cleared whether or not
  4291.      the ProcessInitFrame method succeeded, the important point is
  4292.      whether or not the process was created.  After all, the process
  4293.      cleanup code that DOS runs cleans up stdin/out, etc....
  4294.  
  4295.  
  4296.      Much has been said about METH_PROC_ASSOCIATE -- that there is a
  4297.      method which is called (by default) by the new thread when the
  4298.      process is created, but very little has been said as to what it
  4299.      actually is supposed to do, and, no mention of hooking-up already
  4300.      existing processes with SHADOW process objects has been made.
  4301.  
  4302.      The METH_PROC_ASSOCIATE is the second stage of the two-stage process-
  4303.      creation procedure.  It is responsible for allocating ports for the
  4304.      new process object, opening up any libraries you might want opened,
  4305.      creating any additional ports (like an IDCMP port, for example)
  4306.      that you might want -- generally, any resource allocation that a
  4307.      process has to do up-front.  In addition, once all of this is
  4308.      successfully done, the METH_INIT method is forwarded from the
  4309.      PROCESS_CLASS to the ROOT_CLASS.  The process object, therefore, is
  4310.      not on any system lists until the METH_PROC_ASSOCIATE is called.
  4311.      Also, for your information, the process object is stored in the
  4312.      tc_UserData field of the process -- MAKE SURE YOU DON'T TOUCH IT!
  4313.  
  4314.      In addition, when a program starts, you are already given a process,
  4315.      but no SHADOW object exists for this process.  Creating a process
  4316.      object and calling METH_PROC_ASSOCIATE on it allows you to attach
  4317.      the AmigaDOS process to a SHADOW object, even after AmigaDOS has
  4318.      actually created the process.
  4319.  
  4320.      Startup in your program might look like this:
  4321.  
  4322.           ShadowBase = OpenLibrary("shadow.library", 5);
  4323.           if (!ShadowBase)
  4324.              abort_and_cleanse();
  4325.  
  4326.           procClass = FindShadowClass(PROCESS_CLASS);
  4327.           procObject = DoShadow(procClass, NULL, METH_CREATE, METHOD_END);
  4328.           DropObject(procClass);
  4329.  
  4330.           /*
  4331.            * an object has been created, now initialize it!
  4332.            * Note: procObject is swallowed by this call -- consider it
  4333.            *       never to have really existed as an object....
  4334.            */
  4335.           if (!DoShadow(procObject, NULL, METH_PROC_ASSOCIATE,
  4336.                                           "My program",
  4337.                                           METHOD_END))
  4338.              abort_and_cleanse();
  4339.  
  4340.           /*
  4341.            * Okay, our process is running!
  4342.            */
  4343.  
  4344.      While this is useful if you want to start with something other than
  4345.      a PROCESS_CLASS object, the default PROCESS_CLASS is very useful, and
  4346.      a library call can be used instead of this code, which does
  4347.      essentially the same thing:
  4348.  
  4349.           ShadowBase = OpenLibrary("shadow.library", 5);
  4350.           if (!ShadowBase)
  4351.              abort_and_cleanse();
  4352.           if (!InitOOProgram("My program"))
  4353.              abort_and_cleanse();
  4354.  
  4355.      Note that we do NOT call CreateInstance() for our process object.
  4356.      That, of course, is because we do not want a -new- process, just a
  4357.      new SHADOW object to attach to an already existing process....
  4358.  
  4359.  
  4360.      Along with METH_PROC_ASSOCIATE, you've also come across
  4361.      METH_PROC_HANDLER.  As a default, this is merely the main event loop.
  4362.      The main event loop returns when a ^C is sent to the process, however
  4363.      messages may continue to get handled even after the ^C is sent so
  4364.      that the process can get a shot at deleting its resources....
  4365.  
  4366.      Consider, then, the following complex example:
  4367.           Process A's handler first sends an asynchronous method to
  4368.           process A to, say, draw a random circle.  It then invokes the
  4369.           superclass METH_PROC_HANDLER for default message handling.
  4370.           Within the draw circle routine is a method invocation that,
  4371.           again, invokes an asynchronous method to draw another random
  4372.           circle.  Obviously, this executes ad-nauseum.  Now, the
  4373.           default handler code is careful about buffering the message
  4374.           lists, and therefore allowing the ^C to get processed to start
  4375.           the quit sequence of the program, however, because the program
  4376.           always has a method waiting for it, it can never shutdown.
  4377.           Therefore, your custom handler should also set a global
  4378.           variable when the system METH_PROC_HANDLER returns, and the
  4379.           circle drawing should note this flag and NOT send another
  4380.           asynchronous method when it is sent....
  4381.  
  4382.       Fortunately, however, SHADOW already provides such a flag.
  4383.       In particular, SHADOW_PROC_FLAGS_REMOVED in ATTR_SHADOWPROCESS'
  4384.       sp_flags field is set when the process has returned from the
  4385.       HANDLER (or ParseHandlerFrame) routine.   Note that this is not
  4386.       set within the METH_PROC_HANDLER method, but is set after the
  4387.       routine has exitted by the internal processStartup code....  Thus,
  4388.       if you subclass the METH_PROC_HANDLER routine, the flag may or may
  4389.       not be set when the superclass' METH_PROC_HANDLER method returns to
  4390.       you.  Indeed, as noted, you may even be overriding which method
  4391.       call is actually made (via the ParseHandlerFrame).  It is when this
  4392.       method returns that the flag is actually set.
  4393.  
  4394.  
  4395.       Two more examples for initialization:
  4396.  
  4397.           child = CreateInstance(NULL, PROCESS_CLASS,
  4398.                                        META_CLASS,
  4399.                                        METH_INIT,
  4400.                                        "New Process",
  4401.                                        CurrentProcess(),
  4402.                                          /* Parent process object */
  4403.                                        NULL,
  4404.                                          /* A NULL semaphore --
  4405.                                             strictly speaking, you could
  4406.                                             have gotten away with putting
  4407.                                             the METHOD_END here, it is
  4408.                                             included to remind you that
  4409.                                             the parent object is separate from
  4410.                                             the semaphore field */
  4411.                                        METHOD_END);
  4412.  
  4413.           /*
  4414.            * Yep!  A new process has started up (assuming child not NULL).
  4415.            * We may now use this child in a call to SetupMethodTags() to
  4416.            * allow some of our methods to run in the child process.
  4417.            */
  4418.  
  4419.           success = AddAutoResource(NULL, child, "New Process");
  4420.           DropObject(child)
  4421.           .
  4422.           .
  4423.           .
  4424.  
  4425.           /*
  4426.            * cleanup!
  4427.            */
  4428.           RemoveCurrentProgram(NULL);
  4429.           CloseLibrary(ShadowBase);
  4430.           .
  4431.           .
  4432.           .
  4433.  
  4434.  
  4435.      This example shows off how to use the ParseFrames correctly:
  4436.      Let's say that you have written a METH_PROC_HANDLER for your
  4437.      program's processes in the following fashion:
  4438.  
  4439.           ARGUMENT_TAG  REF_HandleMessages[] =
  4440.                                 {
  4441.                                    ARG_STR,
  4442.                                    ARG_TAGL(sizeof(struct MethodTag)),
  4443.                                    ARG_TAGL(sizeof(struct AttributeTag)),
  4444.                                    RET_NORMAL
  4445.                                 };
  4446.           void HandleProcMessages(METHOD_ARGS, char *baseClassName,
  4447.                                                char *newModuleClassName,
  4448.                                                struct MethodTag *methods,
  4449.                                                struct AttributeTag *attrs)
  4450.           {
  4451.              geta4();    /* Yep, don't forget this! */
  4452.  
  4453.              /*
  4454.               * Allocate a module class for this process.
  4455.               */
  4456.              AddAutoResource(NULL, CreateSubClass(NULL,
  4457.                                                   baseClassName,
  4458.                                                   META_CLASS,
  4459.                                                   newModuleClassName,
  4460.                                                   NULL,
  4461.                                                   methods,
  4462.                                                   attrs,
  4463.                                                   METHOD_END),
  4464.                              newModuleClassName);
  4465.              DoSuperShadow(object, class, MethodID, METHOD_END);
  4466.           }
  4467.  
  4468.      Now, for your own program's process startup, you would naturally use
  4469.      something like the following:
  4470.  
  4471.           /*
  4472.            * Create the process object and attach to the program.
  4473.            */
  4474.           procClass = FindShadow(MY_PROCESS_CLASS);
  4475.           success = DoShadow( DoShadow(procClass,
  4476.                                        NULL,
  4477.                                        METH_CREATE,
  4478.                                        METHOD_END),
  4479.                               NULL,
  4480.                               METH_PROC_ASSOCIATE,
  4481.                               NEW_PROCESS_NAME,
  4482.                               METHOD_END);
  4483.           DropObject(procClass);
  4484.  
  4485.           if (success)
  4486.           {
  4487.              /*
  4488.               * Startup the process and handle all incoming
  4489.               * methods.  Also, create my module's class.
  4490.               */
  4491.              DoShadow(CurrentProcess(), NULL, METH_PROC_HANDLER,
  4492.                                               BASE_MODULE_CLASS,
  4493.                                               MY_NEW_MODULE_CLASS,
  4494.                                               myMethods,
  4495.                                               myAttrs,
  4496.                                               METHOD_END);
  4497.              RemoveCurrentProgram(NULL);
  4498.           }
  4499.  
  4500.           /*
  4501.            * Get out!
  4502.            */
  4503.           CloseLibrary("shadow.library", 5);
  4504.           exit();
  4505.  
  4506.      However, this does not solve a bigger problem -- how to create -new-
  4507.      processes that are not related to an already existing program-process.
  4508.      Obviously, the normal METH_INIT will not work, as the normal
  4509.      METH_PROC_HANDLER is called without any arguments.  We can do one of
  4510.      two things.  The first crack is to call the METH_INIT in the following
  4511.      clumsy fashion:
  4512.  
  4513.           struct MyHandlerFrame {
  4514.              OBJECT         object;
  4515.              META           class;
  4516.              char           *MethodID;
  4517.              char           *baseClass;
  4518.              char           *newClass;
  4519.              METHOD_TAG     *methods;
  4520.              ATTRIBUTE_TAG  *attrs;
  4521.              void           *end;
  4522.           } mhf;
  4523.  
  4524.           mhf.MethodID = NULL;
  4525.           mhf.baseClass = BASE_MODULE_CLASS;
  4526.           mhf.newClass = MY_NEW_MODULE_CLASS_2;
  4527.           mhf.methods = myMethods_2;
  4528.           mhf.attrs = myAttrs_2;
  4529.           mhf.end = METHOD_END;
  4530.  
  4531.           child = CreateInstance(NULL,
  4532.                                  MY_PROCESS_CLASS,
  4533.                                  META_CLASS,
  4534.                                  METH_INIT,
  4535.                                  "New Process #2",
  4536.                                  CurrentProcess(),/* Parent process */
  4537.                                  NULL,            /* No semaphore   */
  4538.                                  NULL,            /* no tags        */
  4539.                                  NULL,            /* No InitFrame   */
  4540.                                  &mhf
  4541.  
  4542.                                  METHOD_END);
  4543.  
  4544.      This is clumsy because each time you want to create a new process,
  4545.      you also have to setup the MyHandlerFrame.  In addition, because
  4546.      the MyHandlerFrame structure has no definitive -length- to it, the
  4547.      METH_INIT cannot be called asynchronously.  A much better way is to
  4548.      redefine the way you call the METH_INIT for the process as follows:
  4549.  
  4550.           /*
  4551.            * ProcessClass' METH_INIT
  4552.            *
  4553.            * NULL if error, else returns the new class-object.
  4554.            *
  4555.            */
  4556.           struct ArgumentTag  REF_InitProcObject[] = {
  4557.                                    ARG_STR,
  4558.                                    ARG_STR,
  4559.                                    ARG_STR,
  4560.                                    ARG_TAGL(sizeof(struct MethodTag)),
  4561.                                    ARG_TAGL(sizeof(struct AttributeTag)),
  4562.                                    RET_OBJ
  4563.                                 };
  4564.  
  4565.           OBJECT InitProcObject(METHOD_ARGS, char *processName,
  4566.                                              char *baseClassName,
  4567.                                              char *newModuleClassName,
  4568.                                              struct MethodTag *methods,
  4569.                                              struct AttributeTag *attrs)
  4570.           {
  4571.              struct MyHandlerFrame {
  4572.                 OBJECT         object;
  4573.                 META           class;
  4574.                 char           *MethodID;
  4575.                 char           *baseClass;
  4576.                 char           *newClass;
  4577.                 METHOD_TAG     *methods;
  4578.                 ATTRIBUTE_TAG  *attrs;
  4579.                 void           *end;
  4580.              } mhf;
  4581.  
  4582.              mhf.MethodID = NULL;  /* default! */
  4583.              mhf.baseClass = baseClassName;
  4584.              mhf.newClass = newModuleClassName;
  4585.              mhf.methods = methods;
  4586.              mhf.attrs = attrs;
  4587.              mhf.end = METHOD_END;
  4588.  
  4589.              /*
  4590.               * This method always runs as a callback!
  4591.               * So we don't have to worry about resource tracking
  4592.               * the variable length HandlerFrame.
  4593.               */
  4594.              return DoSuperShadow(object, class, MethodID,
  4595.                                                  processName,
  4596.                                                  CurrentProcess(),
  4597.                                                  NULL, NULL, NULL,
  4598.                                                  &mhf,
  4599.                                                  METHOD_END);
  4600.           }
  4601.  
  4602.      This is much cleaner, as now to create a new process, all you need to
  4603.      do is the following:
  4604.  
  4605.           child = CreateInstance(NULL, META_CLASS, MY_PROCESS_CLASS,
  4606.                                                    "New Process #3",
  4607.                                                    BASE_MODULE_CLASS,
  4608.                                                    MY_NEW_MODULE_CLASS_3,
  4609.                                                    myMethods_3,
  4610.                                                    myAttrs_3,
  4611.                                                    METHOD_END);
  4612.  
  4613.      This, obviously, looks a lot better, it hides the common code
  4614.      in one place, allowing you to easily create new modules that use the
  4615.      same master process code without duplicating all of the
  4616.      initialization/structure setups....
  4617.  
  4618.  
  4619.      Process and Program Shutdown:
  4620.  
  4621.      The destruction of a process has similar problems as the
  4622.      initialization of processes.  Remember that the destruction of an
  4623.      object -already- occurs in two stages -- the REMOVE and then the
  4624.      DESTROY.  Unfortunately, the METH_DESTROY method really needs to be
  4625.      called as a function, there is, therefore, no guaranteed way to get
  4626.      the METH_DESTROY to occur within the process.  The destroy process,
  4627.      therefore, has been broken up into two pieces:
  4628.           METH_DESTROY -- this sets a DESTROY bit in the ShadowProcess
  4629.                           structure and then signals the task with a
  4630.                           ^C
  4631.           METH_PROC_DISASSOCIATE -- this destroys the ports that were
  4632.                                     allocated by SHADOW, and you should
  4633.                                     subclass this to destroy/close and
  4634.                                     resources you created/opened in the
  4635.                                     ASSOCIATE method.  When this method
  4636.                                     returns the process' name has also
  4637.                                     been freed....  The object's
  4638.                                     destruction is completed with magic
  4639.                                     that will not be discussed here, but
  4640.                                     it is -not- done by the
  4641.                                     METH_DISASSOCIATE method.
  4642.  
  4643.      Neither of these methods should be called by you, neither do they
  4644.      take any arguments.  You may, however, subclass either one of
  4645.      them.  For the most part, anything that you would have normally
  4646.      put into the the METH_DESTROY you should put into the
  4647.      METH_DISASSOCIATE instead....
  4648.      By the way, both of these methods methods call the automatic
  4649.      cleanup procedure, RemoveResources().  The DISASSOCIATE method
  4650.      calls the RemoveResources with the boolean argument set to TRUE,
  4651.      whereas both the METH_REMOVE and the METH_DESTROY call
  4652.      RemoveResources() with the boolean argument set to FALSE.
  4653.  
  4654.      However, process destruction is a two step process, and the first
  4655.      step is the METH_REMOVE signal.  The METH_REMOVE sets a REMOVE
  4656.      bit in the ShadowProcess flags field (to prevent duplicate
  4657.      METH_REMOVE methods) and calls the RemoveResources() call.
  4658.  
  4659.      Process shutdown of a program that has been started by the
  4660.      InitOOProgram() call, or the CREATE-ASSOCIATE pair of calls,
  4661.      needs to be shutdown with the RemoveCurrentProgram() call.  This
  4662.      call takes as an argument the optional semaphore that you
  4663.      started all of the threads with.  As stated before, the preferred
  4664.      manner of starting up is to use the parent process and not a
  4665.      global program semaphore -- but the option exists for you to
  4666.      consider.  The RemoveCurrentProgram() will not return until it
  4667.      is safe for your program to exit.
  4668.  
  4669.  
  4670.      METH_FLAG_OBJECT_AS_DEST
  4671.  
  4672.      There is a specific method flag to deal with process objects --
  4673.      specifically the METH_REMOVE method.  It is completely reasonable
  4674.      to wish that a method for a process actually run in that process.
  4675.      This is the reason for the METH_FLAG_OBJECT_AS_DEST flag for the
  4676.      mtag_flags field of the METHOD_TAG structure.  Specifying the
  4677.      METH_FLAG_OBJECT_AS_DEST instead of METH_FLAG_OBJECT or
  4678.      METH_CLASS, makes the destination of the method the same as the
  4679.      object the method is being invoked on.  In short, invoking a
  4680.      method on a process object, where the method is set with the
  4681.      METH_OBJECT_AS_DEST flag, forces the method to be run in that
  4682.      process object.
  4683.  
  4684.  
  4685.                              Initialization Calls
  4686.  
  4687.      To recap from the PROCESS_CLASS discussion above, a program is
  4688.      initialized and removed in the following fashion under SHADOW:
  4689.  
  4690.           if (ShadowBase = OpenLibrary("shadow.library", 5))
  4691.           {
  4692.              if (InitOOProgram(NEW_PROCESS_NAME))
  4693.              {
  4694.                 /*
  4695.                  * Do what you like!
  4696.                  * Probably a call to HandleMessages()
  4697.                  * HandleMessages() is a macro for:
  4698.                  *  DoShadow(CurrentProcess(), NULL, METH_PROC_HANDLER,
  4699.                  *                                   METHOD_END);
  4700.                  */
  4701.  
  4702.  
  4703.                 /*
  4704.                  * Cleanup!
  4705.                  */
  4706.                 RemoveCurrentProgram(NULL);
  4707.              }
  4708.              CloseLibrary(ShadowBase);
  4709.           }
  4710.  
  4711.  
  4712.  
  4713.                                    CONCLUSION
  4714.  
  4715.  
  4716.  
  4717.      We have finally come to the end of the "Introduction" to SHADOW.
  4718.      We have come quite far during the course of this document.
  4719.      Unfortunately, not everything has been covered in the detail
  4720.      that it really needs to be.  In order to better understand
  4721.      SHADOW, the ShadowLibraryMethods.doc, the ShadowLibraryFuncs.doc,
  4722.      and the ShadowLibFuncs.doc are highly recommended.  The introduction
  4723.      to ShadowLibraryMethods.doc is recommended regardless.
  4724.  
  4725.      In addition, you can browse the basic class descriptions of
  4726.      SHADOW using the browser.  In addition, the source to browser,
  4727.      though somewhat hacked, is included for your use and perusal.
  4728.  
  4729.      If you have understood all of this -- congratulations!  You've done
  4730.      better than I would have!  Try and figure out the included sources,
  4731.      and contact me with cash for your includes and shadow.lib today!
  4732.  
  4733.                David C. Navas
  4734.                SHADOW
  4735.                7 Avocet Dr. #205
  4736.                Redwood Shores, CA 94065
  4737.  
  4738.                jazz@netcom.com
  4739.                BIX: dnavas
  4740.  
  4741.